local _ENV = hf_setenv('ddzctable',true)

------------------------------------------
local skynet = require "skynet"
local socket = require "skynet.socket"
local ddz = require "ddz"
local utils = require "utils"
local proxypack = require "proxypack"
local msgdef = require "msgdef"
local pb = require 'protobuf'
local cjson = require "cjson"
local server_def = require "server_def"
local util = require "util"
local table_def = require "table_def"
local sharedata = require "skynet.sharedata"
local offline_op = require "offline_op"
local game_robot = require "game_robot"
local error_code = require "error_code"
local reason = require "reason"
local cocall = require "cocall"
local constant = require "constant"
local game_def = require "game_def"
local system_store = require "system_store"
local math_abs = math.abs
local math_random = math.random
local table_insert = table.insert
local table_remove = table.remove
local table_unpack = table.unpack
local math_floor = math.floor
local string_format = string.format


--------------------玩家进场状态---------------------
local PLAYER_STATUS_READY = 1
local PLAYER_STATUS_NOREADY = 2
local PLAYER_STATUS_PLAYING = 4

local PLAYER_STATUS_TRUSTEE = 3
--------------------抢地主方式---------------------
local SET_DIZHU_WAY_ROB = 1
local SET_DIZHU_WAY_SCORE = 2

-------------------加倍方式-------------------
local JIABEI_TYPE_GIVEUP = 0
local JIABEI_TYPE_PUTONG = 1
local JIABEI_TYPE_CHAOJI = 2
-------------------面板加倍按钮状态-------------------
local JIABEI_DISABLE = 0
local JIABEI_PUTONG = 1
local JIABEI_CHAOJI = 2
local JIABEI_PUTONG_CHAOJI = 3

-------------------管理---------------------
local handler = {}
local internal = {}

local REGISTER_CLIENT_FD = 0
local ROBOT_CLIENT_FD = -1
local OFFLINE_CLIENT_FD = -2

--------------------机器人管理---------------------
local TIMEOUT_TIMES_TO_TRUSTEE = 2

--------------------明牌管理-----------------------
local MINGPAI_STATUS_NO = 1
local MINGPAI_STATUS_YES = 2

--------------------结算----------------------------
local FENGDING_NOT = 0
local FENGDING_CHANGCI = 1
local FENGDING_JINBI = 2
local POCHAN_NOT = 0
local POCHAN_YES = 1

--------------------离开状态---------------------
local LEAVE_STATUS_REAL = 0 --走人
local LEAVE_STATUS_OFFLINE = 1 --离线

-----------------------游戏状态------------------------
local TABLE_STATUS_REGISTERED = 1 --刚刚注册
local TABLE_STATUS_WAITING_ENTER = 2
local TABLE_STATUS_WAITTING_READY = 3 --等待准备
local TABLE_STATUS_PAY_TICKET = 4
local TABLE_STATUS_CHECK_START = 5
local TABLE_STATUS_ROB_MINGPAI = 6
local TABLE_STATUS_ROB_DIZHU = 7
local TABLE_STATUS_PLAYING = 8
local TABLE_STATUS_GAMEOVER = 9
local TABLE_STATUS_RESTART = 10
local TABLE_STATUS_NODIZHU = 11  --选不出地主，重新开始
local TABLE_STATUS_NODIZHU_RESTART = 12
local TABLE_STATUS_JIABEI = 13

local MAX_NODIZHU_TIMES = 3

function init(params)
    global_configs = nil

    table_players = {}
    player_status_list = {}
    player_info_list = {}

    exit_now = nil
    --玩家的顺序
    ordered_players = {}
    curr_ddz_instance = nil
    robot_manager = {}
    trusteed_players = {}
    --------------------游戏时长管理--------------------
    game_start_time = 0
    game_over_time  = 0


    curr_status = nil
    curr_enter_list = {}
    waiting_enter_timeout = nil
    curr_paid_tickets = {}
    curr_locked_uids = nil
    nodizhu_times = 1
    start_waiting_for_ready_time = nil

    ddz = params.ddz
    trustee_AI = params.trustee_AI
    robot_AI = params.robot_AI
    keep_table = params.keep_table

    self_table_type = nil
    self_table_id = nil
    self_game_type = nil
    self_table_pattern = nil

    this_table_gid = nil
    closing_server = false
    stopping_server = false

    enter_robot_map = {}
    limit_uid = nil
    --触发财神驾到的金额
    caishen_coins_map = {}

    is_curr_have_robot = false
    gm_store_coins_add = 0

    system_result_type = 0 --系统控制类型
    gm_control_uid     = 0 --gm控制玩家uid
    control_win_uid     = 0 --gm控制赢uid
    system_win_lose = 0 --系统输赢

    award_system_flag = false    --是否从库存2(奖金库存扣钱)，若为true，玩家要从奖金库存拿钱
    control_player_uid = nil        --受控玩家的uid
    control_win = false
    control_lose = false
    is_control_systerm_win = false    --是否控制系统赢
    rob_dizhu_score = 0
    hf_set_sentinel_end(_ENV)
end

local function load_config()
    local attr = {}
    global_configs = nil
    global_configs = setmetatable({},{
        __index = function(t,k) 
            if attr[k] then
                return attr[k]
            else
                local v = sharedata.deepcopy(k)
                attr[k] = v
                return v
            end
        end
    })
    _G.global_configs = global_configs
end

local function send_to_gateway(uid,client_fd,...)
    if client_fd <= 0 then
        return
    end
    return utils.send_to_gateway(0,uid,client_fd,...)
end

local function notify_others(action,excepted_uid,msg)
    for uid, fd in pairs(table_players) do
        if uid ~= excepted_uid then
            send_to_gateway(uid,fd,action,msg)
        end
    end
end

local function notify_all(action,msg)
    return notify_others(action,nil,msg)
end

local function get_player_num()
    local n = 0
    for _,_ in pairs(table_players) do
        n = n + 1
    end
    return n
end

local function is_robot(uid)
    return enter_robot_map[uid] or false
end

local function get_extra_bill_info(uid)
    local t = {game_type=self_game_type,table_type=self_table_type}
    local player_info = player_info_list[uid]
    if not player_info then
        return t
    end

    t.client_version = player_info.client_version
    t.channel = player_info.channel
    t.cur_channel = player_info.cur_channel
    t.device_id = player_info.device_id
    t.device_brand = player_info.device_brand
    t.is_robot = is_robot(uid)
    t.os_system = player_info.os_system
    t.platform = player_info.platform
    return t
end


local function is_player_online(uid)
    --在线状态
    if table_players[uid] and table_players[uid] == OFFLINE_CLIENT_FD then
        return false
    end
    return true
end

local function is_player_trustee(uid)
    if trusteed_players[uid] and trusteed_players[uid] >= TIMEOUT_TIMES_TO_TRUSTEE then
       return true
    end 
    return false
end

local function is_player_mingpai(uid)
    local player_info = assert(player_info_list[uid])
    if player_info.mingpai_status == MINGPAI_STATUS_YES then
       return true
    end

    return false
end

local function get_player_state(uid)
    local ready_status = player_status_list[uid]
    if ready_status == PLAYER_STATUS_NOREADY then
        return 0
    elseif ready_status == PLAYER_STATUS_READY then
        return 1
    else
        return 2
    end
end

local function notify_event_status(uid)
    local state = get_player_state(uid)
    local online_state = is_player_online(uid)
    local trustee_state = is_player_trustee(uid)

    local msg = {uid = uid,state = state,online_state = online_state,trustee_state = trustee_state}
    notify_others('ddz.NTF_EVENT',nil,msg)
end

local function notify_score_and_rate_detail()
    assert(curr_ddz_instance)
    for uid, fd in pairs(table_players) do
        local score_rate_detail = curr_ddz_instance:get_score_rate_detail(uid)

        local msg = {
            score_rate_detail = score_rate_detail,
        }
        send_to_gateway(uid,fd,'ddz.NTF_SCORE_AND_RATE_DETAIL',msg)
    end
end

local function notify_player_enter(uid)
    local player_info = assert(player_info_list[uid])
    local cards_count = 0

    if curr_ddz_instance then
        local cards_id = curr_ddz_instance:get_player_card_ids(uid)
        if cards_id then
            cards_count = #cards_id
        end
    end

    local player = {
        uid = uid,
        name = player_info.name,
        cards_count = cards_count,
        position = player_info.position,
        coins = player_info.coins,
        icon = player_info.icon,
        vip_level = player_info.vip_level,
        icon_border = player_info.icon_border,
    }
    
    local play_effect = false
    local enter_word = ""
    local vip_level_conf = utils.getDataBykv(global_configs.vip,"level",player_info.vip_level or 0)
    if vip_level_conf and vip_level_conf.privilege[game_def.VIP_PRIVILEGE_TYPE_SIX] then
        play_effect = true
        enter_word = vip_level_conf.enter_word or ""
    end
    notify_all('ddz.NTF_PLAYER_ENTER',{play_effect = play_effect,player = player,enter_word = enter_word})
end


local function notify_money_changed(uid,chged)
    local ntf = {}
    local flags = 0

    if chged.coins then
        ntf.coins = chged.coins
        flags = flags | (1 << 0)
    end

    if chged.gems then
        ntf.gems = chged.gems
        flags = flags | (1 << 1)
    end

    if chged.roomcards then
        ntf.roomcards = chged.roomcards
        flags = flags | (1 << 2)
    end

    ntf.flags = flags
    local fd = assert(table_players[uid])
    send_to_gateway(uid,fd,'hall.NTF_USER_MONEY',ntf)
    return true
end

--把输赢发送到好友服(平台游戏概况)
local function sendLoseWinToFriendServer(system_lose_win)  
    if system_lose_win == 0 then return end         
    R().friendsvr(1):send('.info_mgr', 'set_room_summary', self_table_type, {table_type = self_table_type, lose_win = system_lose_win})
end  

local function add_task_process(uid,task_type)
    errlog(uid,task_type,'unimplemented function...')
end

local function check_have_robot(player_list)
    for _,uid in pairs(player_list) do
        if is_robot(uid) then
            return true
        end
    end

    return false
end

local function get_personal_system_store_result(robot_type) 
    local player_list = {}
    local player_no_robot_list = {}
    for _,uid in pairs(ordered_players) do
        if not is_robot(uid) then
            table_insert(player_no_robot_list,uid)
        end
        table_insert(player_list,uid)
    end

    local robot_store = global_configs.robot_store[self_table_type]
    local personal_control = global_configs.personal_control
    local is_have_robot = check_have_robot(player_list)
    is_curr_have_robot  = is_have_robot
    local gm_warn = (global_configs.gm_control or {}).warn_coins or 5000000
    local base_warn = robot_store.base_min or 0 

    return system_store.get_personal_system_store_result(self_table_type,player_no_robot_list,gm_warn,is_have_robot,base_warn)
end

local function get_random_robot_uid()
    local robot_ids = {}
    for _,uid in pairs(ordered_players) do
        if is_robot(uid) then
           table_insert(robot_ids,uid) 
        end
    end
    if #robot_ids > 0 then
        local random_index = math_random(1,#robot_ids)
        return robot_ids[random_index]
    end
    return nil
end

local function get_random_player_uid()
    local player_ids = {}
    for _,uid in pairs(ordered_players) do
        if not is_robot(uid) then
           table_insert(player_ids,uid) 
        end
    end
    if #player_ids > 0 then
        local random_index = math_random(1,#player_ids)
        return player_ids[random_index]
    end
    return nil
end

local function get_best_cards_player()
    local special_uid
    local system_result,personal_uid,ctype
    local roomdata = global_configs.roomdata[self_table_type] 
    award_system_flag = false 
    control_player_uid = false
    if roomdata.open_robot and roomdata.robot_type then
        system_result,personal_uid,ctype = get_personal_system_store_result(roomdata.robot_type)
        -- system_result = constant.SYSTEM_STORE_WIN
        -- system_result_type = system_result
        dbglog(gm_control_uid)
        local control_type = ctype
        special_uid = get_random_robot_uid()
        gm_control_uid = personal_uid
        if system_result then
            if system_result == constant.PERSONAL_STORE_WIN and personal_uid then   --控制玩家赢，给该玩家发最大牌型
                special_uid = personal_uid
                control_player_uid = personal_uid
            elseif system_result == constant.PERSONAL_STORE_LOSE and personal_uid then  --控制玩家输，给机器人发最大牌型
                special_uid = get_random_robot_uid(second_uid)
                control_player_uid = personal_uid
            elseif system_result == constant.SYSTEM_STORE_WIN then   --机器人拿最大牌
                --随机发牌策略
                special_uid = get_random_robot_uid()
            elseif system_result == constant.SYSTEM_STORE_LOSE then       --玩家拿最大牌
                special_uid = get_random_player_uid()
            elseif system_result == constant.AWARD_STORE_LOSE then
                special_uid = get_random_player_uid()
                if special_uid then
                    award_system_flag = true         
                end
            end
        end
    end
    control_win_uid = special_uid
    return special_uid
end

local function send_table_base_score_and_rate(uid)
    if not curr_ddz_instance then
        return
    end
    
    local base_score,rate = curr_ddz_instance:get_base_score_and_rate()
    local rsp = {
        base_score = base_score,
        rate = rate,
    }
    
    if uid then
        local fd = assert(table_players[uid])
        if fd > 0 then
            --send_to_gateway(uid,fd,'table.NTF_SCORE_AND_RATE',rsp)
        end
        return
    end

    for uid,fd in pairs(table_players) do
        if fd > 0 then
           --send_to_gateway(uid,fd,'table.NTF_SCORE_AND_RATE',rsp)
        end
    end
end

local function set_table_base_rate(ddz_instance)
    assert(ddz_instance)
    local roomdata = global_configs.roomdata
    assert(roomdata[self_table_type])
    ddz_instance:set_rate(tonumber(roomdata[self_table_type].rate))
end

local function set_table_base_score(ddz_instance)
    assert(ddz_instance)
    local roomdata = global_configs.roomdata
    assert(roomdata[self_table_type])
    ddz_instance:set_base_score(tonumber(roomdata[self_table_type].dizhu))
end 

local function watch_session(game_session,uid,observing)
    if game_session <= 0 then
        return
    end
    local gateway_id = game_session >> 31
    local request
    if observing then
        request = 'observe_fd'
    else
        request = 'unobserve_fd'
    end

    R().gateway(gateway_id):send('.watchdog','tablesvr',request,R().get_source(),game_session,uid)
end

local function is_trusteed(uid)
    local t = trusteed_players[uid]
    return t and t >= TIMEOUT_TIMES_TO_TRUSTEE
end

local function get_response_list(ddz_instance)
    local curr_card_suit_type,curr_card_suit,key,curr_play_uid = ddz_instance:get_last_card_suit() 
    --notify
    local players_card_id_list = {}
    local players_info_list = {}
    local common = {
        dizhu_uid = ddz_instance:get_dizhu_uid(),
        dizhu_card_id_list = ddz_instance:get_dizhu_card_ids(),
        next_player_uid = ddz_instance:get_next_player_uid(),
        must_play = 0,
        end_time = ddz_instance:get_relative_play_end_time(),
        curr_card_suit_type = curr_card_suit_type,
        curr_card_suit = curr_card_suit,
        curr_card_suit_key = key,
        curr_play_uid = curr_play_uid,
    }

    if ddz_instance:is_must_play(ddz_instance:get_next_player_uid()) then
        common.must_play = 1
    end

    local copy = function(common)
        local o = {}
        for k,v in pairs(common) do
            o[k] = v
        end
        return o
    end

    for uid,_ in pairs(table_players) do
        players_card_id_list[uid] = assert(ddz_instance:get_player_card_ids(uid))
    end

    for _,uid in ipairs(ordered_players) do
        local r = {}
        local cards_ids = players_card_id_list[uid]
        local player_info = assert(player_info_list[uid])
        local r = {
            uid = uid,
            name = player_info.name,
            cards_count = #cards_ids,
            position = player_info.position,
            state = get_player_state(uid),
            coins = player_info.coins,
            icon = player_info.icon,
            sex = player_info.sex,
            online_state = is_player_online(uid),
            trustee_state = is_player_trustee(uid),
            mingpai_state = is_player_mingpai(uid),
            vip_level = player_info.vip_level,
            icon_border = player_info.icon_border,
        }

        table_insert(players_info_list,r)
    end

    local rsp_list = {}
    print_r(players_info_list)
    for position,uid in ipairs(ordered_players) do
        local rsp = copy(common)
        local tmp_list = {}
        for _,v in ipairs(players_info_list) do
            table_insert(tmp_list,copy(v))
        end
        
        local self_info = assert(tmp_list[position])
        assert(self_info.uid == uid)
        self_info.card_id_list = assert(players_card_id_list[uid])
        self_info.cards_count = -1

        rsp.players_info_list = tmp_list
        rsp_list[uid] = rsp
    end 

    return rsp_list
end

local function get_card_note_info(uid,ddz_instance)
    local other_player_cards = ddz_instance:get_other_player_cards(uid)
    local other_player_record = {}

    local records = ddz_instance:get_player_record()
    for _uid,player_record in pairs(records) do
        if uid ~= _uid then
            local record = { uid = _uid,player_records = {} }
            for _,card_record in pairs(player_record) do
                table_insert(record.player_records,{card_list = card_record})
            end

            table_insert(other_player_record,record)
        end
    end

    return {other_player_cards = other_player_cards,other_player_record = other_player_record}
end

---------------------------------------------------------------------------
local function lock_one_player(uid,table_gid)
    local ok,succ = R().exdbsvr(1):call('.tlock_mgr','set_on_table',uid,table_gid)
    if not ok then
        errlog(uid,'failed to set_on_table')
        return
    end

    if not succ then
        return -10
    end
    if not is_robot(uid) then
        R().friendsvr(1):send('.info_mgr','update_info',uid,{curr_game_type=self_game_type,curr_table_type=self_table_type})
    end
    return 0
end

local function get_all_human_uids()
    local uids = {}
    for uid,fd in pairs(table_players) do
        if not is_robot(uid) then
            table_insert(uids,uid)
        end
    end
    return uids
end

local function _lock_all_players(locked_uids,uids)
    local tasks = {}
    for _,uid in pairs(uids) do
        table_insert(tasks,{ f = lock_one_player,id = uid,params = {uid,this_table_gid}})
        locked_uids[uid] = false
    end

    local ok,results = cocall(5,tasks)
    if not ok then
        errlog('failed to cocall',tostring_r(results))
        return
    end

    local succ = true
    for uid,r in pairs(results) do
        if r == 0 then
            locked_uids[uid] = true
        else
            succ = false
            errlog(uid,'failed to set on table ...',this_table_gid)
        end
    end

    if not succ then
        for uid,r in pairs(results) do
            --有人未成功，则释放已经成功的人,以免坑了其它玩家
            --有可能玩家在设置成功后，返回到该节点的时候超时了，因而该玩家也需要解开锁
            --反正如果解锁的时候，玩家不是当前的table_gid也是无法被解开的
            R().exdbsvr(1):send('.tlock_mgr','unset_on_table',uid,this_table_gid)
        end
    end

    return succ
end

local function lock_all_players()
    if curr_locked_uids then
        return true
    end
    curr_locked_uids = {}
    local uids = get_all_human_uids()
    return _lock_all_players(curr_locked_uids,uids) 
end

local function lock_all_players_on_register(uids)
    assert(not curr_locked_uids)
    curr_locked_uids = {}
    return _lock_all_players(curr_locked_uids,uids) 
end

--玩这实时输赢op
function realTimeWinLose(uid)
    if is_robot(uid) then return end
    local player_info = player_info_list[uid]
    if not player_info.enter_time then 
        errlog("__________player_info.enter_time__是空的____", uid, player_info)
        return 
    end     
    local table_gid = player_info.enter_time .. "_" .. self_table_id .."_"..uid
    local leave_time = util:get_now_time()  --真正离开游戏了 
    if player_info.all_win_lose == 0 and player_info.all_free == 0 then 
        --没产生流水就不记录了
        return
    end
    local data = {
        op = "user_leave_game",
        uid = uid,
        table_gid = table_gid, --唯一标记
        table_type = self_table_type,
        name = player_info.name,        
        enter_coins = player_info.enter_coins, --进入时身上金币
        enter_safe_coins = player_info.enter_safe_coins, --进入时保险箱金币 
        enter_time = player_info.enter_time, --进入时间
        leave_time = leave_time, --离开时间
        leave_coins = player_info.coins, --离开时身上金币
        leave_safe_coins = player_info.safe_box, --离开时保险箱金币
        win_lose = player_info.all_win_lose, --在玩游戏内产生的输赢（
        free = player_info.all_free, --玩游戏交上的台费
    }
    dbglog("_______user_leave_game_____",  data)
    billlog(data)
end

local function unlock_one_player(uid)
    if is_robot(uid) then return end
    --这里不用call的原因是，即使解锁失败了也没有办法，所以暂时就只send
    R().exdbsvr(1):send('.tlock_mgr','unset_on_table',uid,this_table_gid)
    R().friendsvr(1):send('.info_mgr','update_info',uid,{curr_game_type=0})
    

end

local function unlock_all_players()
    local uids = get_all_human_uids()
    for _,uid in pairs(uids) do
        unlock_one_player(uid)
        realTimeWinLose(uid)
    end
    curr_locked_uids = nil
end

local function are_all_players_locked()
    assert(curr_locked_uids)
    for _,r in pairs(curr_locked_uids) do
        if not r then
            return false
        end
    end

    return true
end
---------------------------------------------------------------------------
local function trigger_event(action,...)
    for _,AIobj in pairs(robot_manager) do
        local f = AIobj[action]
        if f then
            f(AIobj,...)
        end
    end
end

local function get_response_dizhu_history()
    local history_list = curr_ddz_instance:get_rob_dizhu_history_list()
    --参与次数:0是不叫，1是叫，2是抢,3是不抢
    local tmp_map = {}
    local is_call = true
    for _,r in ipairs(history_list) do
        local uid = r.uid
        --[[if r.is_rob then
            if is_call then
                tmp_map[uid] = 1
            else
                tmp_map[uid] = 2
            end
            is_call = false
        else
            if is_call then
                --放弃，正在叫地主，这是不叫
                tmp_map[uid] = 0
            else
                --放弃，不在叫地主，这是不抢
                tmp_map[uid] = 3
            end
        end]]
        tmp_map[uid] = r.score
    end

    local set_dizhu_history_list = {}
    for uid,status in pairs(tmp_map) do
        table_insert(set_dizhu_history_list,{
            uid = uid,
            status = status,
        })
    end

    return set_dizhu_history_list
end

local function get_player_enter_data(uid)
     --先拉取玩家信息
    local ok,base_data = R().basesvr({key=uid}):call('.msg_handler','get_base_data',uid)
    if not ok then
        errlog(uid,'failed to get base_data',uid)
        return
    end
   
    --再拉取玩家的登入信息 跟大厅拉取
    local ok,enter_data = R().hallsvr({key=uid}):call('.msg_handler','get_enter_data',uid)
    if not ok then
        errlog(uid,'failed to get enter_data')
        enter_data = {}
    end
    
    local player_data = {
        uid = base_data.uid,
        name = enter_data.name or '',
        coins = base_data.coins,
        icon = enter_data.icon or '',
        sex = enter_data.sex or 1,
        vip_level = base_data.vip_level or 0,
        icon_border = enter_data.icon_border,
        safe_box = base_data.safe_box,
    }

    return player_data    
end

local function enter(uid,client_fd,msg)
    print('now enter ========================',uid,client_fd)
    if not table_players[uid] then
        errlog(uid,'that player have not been registed yet')
        return false
    end

    if client_fd == ROBOT_CLIENT_FD then
        enter_robot_map[uid] = true
    end

    if client_fd ~= ROBOT_CLIENT_FD then
        local data = get_player_enter_data(uid)
        if not data then
            errlog(uid,'ctable failed to get_player_enter_data')
            return false
        end
        local player_info = player_info_list[uid]
        if not player_info then
            errlog(uid,'ctable failed to get player_info')
            return false
        end

        player_info.name = data.name
        player_info.icon = data.icon
        player_info.coins = data.coins
        player_info.sex = data.sex
        player_info.vip_level = data.vip_level
        player_info.icon_border = data.icon_border
        player_info.client_version = msg.client_version
        player_info.channel = msg.channel
        player_info.cur_channel = msg.cur_channel
        player_info.device_id = msg.device_id
        player_info.device_brand = msg.device_brand
        player_info.safe_box = data.safe_box
        player_info.enter_coins = data.coins
        player_info.enter_time = os.time()
        player_info.enter_safe_coins = data.safe_box
        player_info.all_win_lose = 0
        player_info.all_free = 0
        player_info.os_system = data.os_system
        player_info.platform = data.platform
    end

    table_players[uid] = client_fd

    local roomdata = assert(global_configs.roomdata)
    local enter_info = {}
    enter_info.has_card_note = player_info_list[uid].has_card_note

    if not curr_ddz_instance then
        local players_info_list = {}
        for _uid,player_info in pairs(player_info_list) do
                table_insert(players_info_list,{
                    uid = _uid,
                    name = player_info.name,
                    position = player_info.position,
                    coins = player_info.coins,
                    icon = player_info.icon,
                    sex = player_info.sex,
                    state = get_player_state(_uid),
                    online_state = is_player_online(_uid),
                    trustee_state = is_player_trustee(_uid),
                    mingpai_state = is_player_mingpai(_uid),
                    vip_level = player_info.vip_level,
                    icon_border = player_info.icon_border
                })
        end 

        enter_info.waiting_table_status = {
            players_info_list = players_info_list,
        }
        enter_info.game_status = 0
    else
        local last_round_records = curr_ddz_instance:get_last_round_records()
        local rsp_list = get_response_list(curr_ddz_instance)
        enter_info.game_status = 1        
        enter_info.table_status = assert(rsp_list[uid])
        enter_info.last_card_records = last_round_records

        if curr_status > TABLE_STATUS_ROB_MINGPAI then
            local setting_uid,cur_count,setting_end_time,set_dizhu_way = curr_ddz_instance:get_setting_info()
            -- if is_player_trustee(setting_uid) then
            --     setting_end_time = 0
            -- end
            local set_status = {
                uid = setting_uid,
                cur_count = cur_count,
                end_time = setting_end_time,
                set_dizhu_way = set_dizhu_way,
                rob_count = curr_ddz_instance:get_rob_count(),
                max_score = curr_ddz_instance:get_rob_rate(),
            }
            enter_info.set_dizhu_status = set_status
        end

        if curr_status == TABLE_STATUS_ROB_DIZHU then
            enter_info.set_dizhu_history_list = get_response_dizhu_history()
        end
        enter_info.score_rate_detail = curr_ddz_instance:get_score_rate_detail(uid)
    end

    return true,{table_type = self_table_type, game_type = 1, ddz_enter_info = enter_info}
end

local function finish_game()
    local ok,result = curr_ddz_instance:get_game_result()
    if not ok then
        return
    end

    trigger_event('on_game_over')

    return result
end

local function set_all_unready()
    for uid,status in pairs(player_status_list) do
        assert(status == PLAYER_STATUS_PLAYING)
        player_status_list[uid] = PLAYER_STATUS_NOREADY
    end
end

local function set_all_untrustee()
    for uid,status in pairs(trusteed_players) do
        trusteed_players[uid] = nil
    end    
end

local function check_play_update_task(uid,card_suit_type)
    -- if card_suit_type == ddz.CARD_SUIT_TYPE_FEIJI or 
    --     card_suit_type == ddz.CARD_SUIT_TYPE_FEIJIDAICIBANG then
    --     add_task_process(uid,constant.TASK_PLAY_FEIJI)
    -- elseif card_suit_type == ddz.CARD_SUIT_TYPE_WANGZHA then
    --     notify_score_and_rate_detail()
    --     add_task_process(uid,constant.TASK_PLAY_WANGZHA)
    -- elseif card_suit_type == ddz.CARD_SUIT_TYPE_ZHADAN or
    --     card_suit_type == ddz.CARD_SUIT_TYPE_RUANZHA then
    --     print('fff',constant.TASK_PLAY_ZHADAN)
    --     notify_score_and_rate_detail()
    --     add_task_process(uid,constant.TASK_PLAY_ZHADAN)       
    -- end    
end

local function set_trustee(uid,setting)
    if setting then
        trusteed_players[uid] = TIMEOUT_TIMES_TO_TRUSTEE
    else
        trusteed_players[uid] = nil
    end
end

local function play(uid,card_suit,card_suit_type,key)
    if not curr_ddz_instance then
        return false,{result = -1}
    end

    --考虑到不出牌的情况
    if #card_suit == 0 then
        local ok,ret = curr_ddz_instance:donot_play(uid)
        if not ok then
            skynet.error('failed to donot_play',uid)  
            return false,{result = ret}
        end

        local next_player_uid = curr_ddz_instance:get_next_player_uid()
        local must_play = 0
        if curr_ddz_instance:is_must_play(next_player_uid) then
            must_play = 1
        end

        trigger_event('on_play',uid,card_suit)
        
        local end_time = curr_ddz_instance:get_relative_play_end_time()
        -- if is_player_trustee(next_player_uid) then
        --     end_time = 0
        -- end

        return true,{
            result = 0,
            next_player_uid = next_player_uid,
            must_play = must_play,
            end_time = end_time,
        }
    end
    local ok,payload = curr_ddz_instance:play(uid,card_suit,card_suit_type,key)
    if not ok then
        return false,{result = payload}
    end

    local next_player_uid = curr_ddz_instance:get_next_player_uid()
    local must_play = 0
    if curr_ddz_instance:is_must_play() then
         must_play = 1
    end

    local card_suit_type = payload.card_suit_type
    -- check_play_update_task(uid,card_suit_type)
    
    trigger_event('on_play',uid,payload.card_suit)

    local end_time = curr_ddz_instance:get_relative_play_end_time()
    -- if is_player_trustee(next_player_uid) then
    --     end_time = 0
    -- end
    return true,{
        result = 0,
        card_suit_type = payload.card_suit_type,
        card_suit = payload.card_suit,
        next_player_uid = next_player_uid,
        must_play = must_play,
        end_time = end_time,
        card_suit_key = payload.card_suit_key,
        original_card_suit = payload.original_card_suit,
        over = payload.over,
    }
end

local function rob_dizhu(uid,score,is_rob)
    if not curr_ddz_instance then
        return false,-1000
    end

    if not curr_ddz_instance:is_rob_dizhu() then
        errlog('is not a robbing time')
        return false,-2000
    end

    if curr_ddz_instance:get_setting_uid() ~= uid then
        return false,-3000
    end

    local rob_count = curr_ddz_instance:get_rob_count()
    local rsp_is_rob
    if is_rob then
        if rob_count == 0 then
            rsp_is_rob = 1
        else
            rsp_is_rob = 3
        end
    else
        if rob_count == 0 then
            rsp_is_rob = 0
        else
            rsp_is_rob = 2
        end
    end

    print(uid,"score",score,"is_rob",is_rob)
    local forced = nodizhu_times >= MAX_NODIZHU_TIMES
    curr_ddz_instance:rob_dizhu(uid,score,is_rob,forced)
    rob_dizhu_score = score
    trigger_event('on_rob_dizhu',uid,score,is_rob)

    local setting_uid,cur_count,setting_end_time,set_dizhu_way = curr_ddz_instance:get_setting_info()
    -- if is_player_trustee(setting_uid) then
    --     setting_end_time = 0
    -- end

    local set_status = {
        uid = setting_uid,
        cur_count = cur_count,
        end_time = setting_end_time,
        set_dizhu_way = set_dizhu_way,
        rob_count = curr_ddz_instance:get_rob_count(),
        max_score = curr_ddz_instance:get_rob_rate(),
    }

    local msg = {
        pre_uid = uid,
        score = score,
        is_rob = rsp_is_rob,
        set_dizhu_status = set_status,
    }
    
    notify_others('ddz.NTF_ROBDIZHU',uid,msg)

    local rsp = {result = 0,score = score,is_rob = rsp_is_rob,set_dizhu_status = set_status}
    send_to_gateway(uid,table_players[uid],'ddz.RSP_ROBDIZHU',rsp)

    return true
end

local function set_ready(uid)
    assert(player_status_list[uid])
    player_status_list[uid] = PLAYER_STATUS_READY

    notify_event_status(uid)
    return true
end


------------------------------------托管相关---------------------------------
--托管出牌
local function trustee_play()
    local uid = curr_ddz_instance:get_next_player_uid()
    local play_params = trustee_AI.trustee_play(uid,curr_ddz_instance)
	dbglog('player---------------------',uid,
        string.format('[%s]',tostring_r(play_params)))
    local ok,rsp = play(uid,table_unpack(play_params))
    print_r(rsp)

    local fd = table_players[uid]
    send_to_gateway(uid,fd,'ddz.RSP_PLAY',rsp)
    assert(ok)
    local msg = {
        player_uid = uid,
        card_suit_type = rsp.card_suit_type,
        card_suit = rsp.card_suit,
        next_player_uid = rsp.next_player_uid,
        must_play = rsp.must_play,
        end_time = rsp.end_time,
        card_suit_key = rsp.card_suit_key,
        original_card_suit = rsp.original_card_suit,
        over = rsp.over,
    }
    notify_others('ddz.NTF_PLAY',uid,msg)
end

local function check_trusteed_player(curr_time)
    local uid = curr_ddz_instance:get_next_player_uid()
    local timeout_times = trusteed_players[uid] or 0

    if timeout_times >= TIMEOUT_TIMES_TO_TRUSTEE then
        trustee_play()
        return
    elseif curr_time >= curr_ddz_instance:get_play_end_time() then
        timeout_times = timeout_times + 1
        trusteed_players[uid] = timeout_times
        trustee_play()

        if timeout_times >= TIMEOUT_TIMES_TO_TRUSTEE then
            notify_event_status(uid)
        else
            send_to_gateway(uid,table_players[uid],'ddz.NTF_PLAY_TIMEOUT',{
                times = 1,total_times = TIMEOUT_TIMES_TO_TRUSTEE})
        end
        return
    end

    return
end
------------------------------------------------------------------------------
local function check_all_enter(curr_time)
    if curr_time >= waiting_enter_timeout then
        return true
    end

    for uid,fd in pairs(table_players) do
        if fd == REGISTER_CLIENT_FD then
            return false
        end
    end

    return true
end

local function can_ready(uid)
    if curr_status ~= TABLE_STATUS_WAITTING_READY then
        errlog("curr_status is not waiting ready !! ",curr_status)
        return false
    end

    local player_info = assert(player_info_list[uid])
    local roomdata = assert(global_configs.roomdata)
    local coins = player_info.coins
    local min = assert(roomdata[self_table_type].min)
    local max = assert(roomdata[self_table_type].max)
    if coins < min then
        return false
    elseif max ~= 0 and coins > max then
        return false
    else
        return true
    end
end

local function are_all_players_ready()
    if #ordered_players < ddz.MAX_PLAYER_NUM then
        return false
    end

    for _,status in pairs(player_status_list) do
        if status ~= PLAYER_STATUS_READY then
            return false
        end
    end

    return true
end

local function pay_one_ticket(uid,coins,is_robot)
    local ok,succ,ret = R().basesvr({key=uid}):call('.msg_handler',
        'pay_one_ticket',uid,coins,reason.PAY_FEE,get_extra_bill_info(uid))
    if not ok then
        errlog(uid,'failed to pay ticket',coins,succ)
        return
    end
    if not succ then
        errlog(uid,'failed to pay ticket',coins)
        return
    end

    return {
        curr_coins = ret.curr,
        has_card_note = ret.has_card_note,
        chged = ret.chged
    }
end

local function pay_all_tickets(ticket_list)
    curr_paid_tickets = {}

    local payers = {}
    for _, ticket in pairs(ticket_list) do
        local uid = ticket.uid
        local cost = ticket.cost
        curr_paid_tickets[uid] = false
        if is_robot(uid) then
            local player_info = player_info_list[uid]
            player_info.coins = player_info.coins - cost
            player_info.has_card_note = false
            curr_paid_tickets[uid] = true
            notify_money_changed(uid,{coins = player_info.coins})
            notify_all('ddz.NTF_MONEY_CHANGE',{uid = uid,coins = player_info.coins})
        else
            table_insert(payers,{ f = pay_one_ticket,id = uid,params = {uid,cost,is_robot(uid)} })
        end
    end

    local ok,results = cocall(5,payers)
    if not ok then
        errlog('failed to cocall',tostring_r(results))
    end

    for uid,r in pairs(results) do
        if r and r.curr_coins then
            curr_paid_tickets[uid] = true
            notify_money_changed(uid,{coins = r.curr_coins})
            notify_all('ddz.NTF_MONEY_CHANGE',{uid = uid,coins = r.curr_coins})
            local player_info = assert(player_info_list[uid])
            player_info.coins = r.curr_coins
            player_info.has_card_note = r.has_card_note
        else
            errlog(uid,'failed to pay ticket',uid,r)
        end
    end
end

local function are_all_tickets_paid()
    if not curr_paid_tickets then
        errlog('failed to check curr_paid_tickets is nil')
        return false
    end
    for _,paid in pairs(curr_paid_tickets) do
        if not paid then
            return false
        end
    end

    return true
end


local function can_start()
    if curr_ddz_instance then
        return false,-1
    end

    if #ordered_players < ddz.MAX_PLAYER_NUM then
        return false,-2
    end

    for _,status in pairs(player_status_list) do
        if status ~= PLAYER_STATUS_READY then
            return false,-3
        end
    end
    local roomdata = global_configs.roomdata[self_table_type]
    local ddz_instance = ddz:new()
    ddz_instance:init(self_table_type,roomdata.classic_rate)

    for _,uid in ipairs(ordered_players) do
        ddz_instance:enter(uid)
    end

    local ok = ddz_instance:check_and_start()
    if not ok then
        return false,-3
    end

    -- ddz_instance:shuffle()
    local special_uid = get_best_cards_player()
    if system_result_type == constant.PERSONAL_STORE_LOSE or system_result_type == constant.SYSTEM_STORE_WIN then --击杀
        ddz_instance:deal(special_uid,ddz.PLAY_MODE.KILL)
    elseif system_result_type == constant.PERSONAL_STORE_WIN or system_result_type == constant.SYSTEM_STORE_LOSE or system_result_type == constant.AWARD_STORE_LOSE then --放水
        ddz_instance:deal(special_uid,ddz.PLAY_MODE.DIVE)
    else
        ddz_instance:deal()
    end

    set_table_base_score(ddz_instance)
    
    set_table_base_rate(ddz_instance)

    for _uid,status in pairs(player_status_list) do
        player_status_list[_uid] = PLAYER_STATUS_PLAYING
    end

    return ddz_instance
end

local function mingpai(uid,rate)
    --mingpai_players[uid] = true
    local record_list = curr_ddz_instance:get_card_record_list()
    if record_list and #record_list > 0 then
        return false
    end

    curr_ddz_instance:set_mingpai(uid,rate)
    
    --通知其他玩家
    local ntf = {
        uid = uid,
        card_id_list = assert(curr_ddz_instance:get_player_card_ids(uid))
    }
    dbglog("NTF_MINGPAI",ntf)
    notify_all('ddz.NTF_MINGPAI',ntf)
    notify_score_and_rate_detail()
    return true
end

local function notify_start(curr_ddz_instance)
    local rsp_list = get_response_list(curr_ddz_instance)
    print('now notify game is started....')
    -- local setting_uid,cur_count,setting_end_time,set_dizhu_way = curr_ddz_instance:get_setting_info()
    -- local set_status = {
    --     uid = setting_uid,
    --     cur_count = cur_count,
    --     end_time = setting_end_time,
    --     set_dizhu_way = set_dizhu_way,
    --     rob_count = curr_ddz_instance:get_rob_count(),
    -- }
    
    for uid,fd in pairs(table_players) do
        local detail = curr_ddz_instance:get_score_rate_detail(uid)
        local  msg = {
            table_status = rsp_list[uid],
            --set_dizhu_status = set_status,
            score_rate_detail = detail,
            has_card_note = player_info_list[uid].has_card_note
        }
        send_to_gateway(uid,fd,'ddz.NTF_START',msg)
        dbglog(msg)
    end 
    for uid,fd in pairs(table_players) do
        mingpai(uid, 100)
    end
    --TODO 此处应该封装到ntf_start和rsp_enter里面去
   -- send_table_base_score_and_rate()
end

local function notify_dizhu_status()
    local setting_uid,cur_count,setting_end_time,set_dizhu_way = curr_ddz_instance:get_setting_info()
    -- if is_player_trustee(setting_uid) then
    --     setting_end_time = 0
    -- end
    local set_status = {
        uid = setting_uid,
        cur_count = cur_count,
        end_time = setting_end_time,
        set_dizhu_way = set_dizhu_way,
        rob_count = curr_ddz_instance:get_rob_count(),
        max_score = curr_ddz_instance:get_rob_rate(),
    }
    local  msg = {
        set_dizhu_status = set_status,
    }
    notify_all('ddz.NTF_DIZHUSTATUS',msg)
end

local function check_start(curr_time)
    dbglog("check_start")
    local ddz_instance,msg = can_start()
    if not ddz_instance then
        dbglog('failed to can_start()',msg)
        return
    end

    print('now start the game...')
    curr_ddz_instance = ddz_instance

    return true
end

local function check_rob_dizhu(curr_time)
    local is_over,dizhu_uid = curr_ddz_instance:get_rob_dizhu_result()
    if is_over then
        return true,dizhu_uid
    end

    local uid,_,_,_,end_time = curr_ddz_instance:get_setting_info()
    if is_player_trustee(uid) then
        rob_dizhu(uid,0,false)
    else
        if curr_time >= end_time then
            --现在还没人抢，就系统代劳不抢了
            rob_dizhu(uid,0,false)
        end
    end

    return false
end

local function get_jiabeicard(uid)
    local ok,base_data = R().basesvr({key=uid}):call('.msg_handler',
        'get_base_data',uid)
    if not ok then
        errlog(uid,'failed to pay ticket',coins,succ)
        return
    end
    
    return {
        jiabei_cards = base_data.jiabei_cards
    }
end

local function notify_rob_dizhu_over()
    local rsp = curr_ddz_instance:get_notify_dizhu_msg()
    notify_all('ddz.NTF_SETDIZHU',rsp)
    local jiabei_end_time = curr_ddz_instance:get_jiabei_end_time()
    jiabei_end_time = jiabei_end_time - util.get_now_time()
    -- if jiabei_end_time < 0 then
    --     jiabei_end_time = 0
    -- end
    
    --查询玩家加倍卡
    local players = {}
    for uid,fd in pairs(table_players) do
        if fd ~= ROBOT_CLIENT_FD then
            table_insert(players,{ f = get_jiabeicard,id = uid,params = {uid} })  
        end
    end
    
    local ok,results = cocall(5,players)
    if not ok then
        errlog('failed to cocall',tostring_r(results))
        notify_all('ddz.NTF_JIABEI_PANEL',{end_time = jiabei_end_time})
        return
    end

    --下发否每个人加倍状态
    for uid,r in pairs(results) do
        if r and r.jiabei_cards then
            local jiabei_cards = r.jiabei_cards
            local jiabei_type = JIABEI_DISABLE
            assert(player_info_list[uid],"No this player!")
            assert(player_info_list[uid].coins,"coins invalid!")
            if player_info_list[uid].coins > 8000 then
                jiabei_type = JIABEI_PUTONG
            end
            if jiabei_cards > 1 then
                jiabei_type = JIABEI_CHAOJI
            end
            if player_info_list[uid].coins > 8000 and jiabei_cards > 1 then
                jiabei_type = JIABEI_PUTONG_CHAOJI
            end

            -- if is_player_trustee(uid) then
            --     jiabei_end_time = 0
            -- end

            local ntf = {
                jiabei_type = jiabei_type,
                end_time = jiabei_end_time
            }

            local fd = assert(table_players[uid])
            send_to_gateway(uid,fd,'ddz.NTF_JIABEI_PANEL',ntf)
        else
            -- if is_player_trustee(uid) then
            --     jiabei_end_time = 0
            -- end

            local ntf = {
                jiabei_type = JIABEI_DISABLE,
                end_time = jiabei_end_time
            }

            local fd = assert(table_players[uid])
            send_to_gateway(uid,fd,'ddz.NTF_JIABEI_PANEL',ntf)
        end
    end

end

local function jiabei(uid,type)
    local player_info = assert(player_info_list[uid])
    if type == JIABEI_TYPE_PUTONG then
        -- if player_info.coins < 8000 then
        --     send_to_gateway(uid,table_players[uid],'ddz.RSP_JIABEI',{result = error_code.GOLD_IS_NOT_ENOUGH})
        --     return
        -- end
    elseif type == JIABEI_TYPE_CHAOJI then
        --TODO 消耗道具
        local ok,ret = R().basesvr({key=uid}):call('.msg_handler','reduce_jiabeicards',
            uid,1,reason.CHAOJI_JIABEI)
        if not ok then
            send_to_gateway(uid,table_players[uid],'ddz.RSP_JIABEI',{result = error_code.JIABEI_CRAD_IS_LESS})
            return
        end
    end
    
    curr_ddz_instance:jiabei(uid,type)
    notify_all('ddz.NTF_JIABEI',{uid = uid,type = type})
    if type == JIABEI_TYPE_PUTONG or type == JIABEI_TYPE_CHAOJI then
        notify_score_and_rate_detail()
    end
end

local function check_jiabei_over(curr_time)
    if curr_ddz_instance:check_all_player_jiabei() then
        return true
    end

    local jiabei_player_list = curr_ddz_instance:get_jiabei_player_list()
    local jiabei_end_time = curr_ddz_instance:get_jiabei_end_time()
    --托管的玩家直接放弃加倍
    for _,uid in pairs(ordered_players) do
        if is_player_trustee(uid) then
            if not jiabei_player_list[uid] then
                jiabei(uid,JIABEI_TYPE_GIVEUP)
            end
        end
    end
    if curr_time > jiabei_end_time then
        for _,uid in pairs(ordered_players) do
            if not jiabei_player_list[uid] then
                jiabei(uid,JIABEI_TYPE_GIVEUP)
            end
        end
        return true
    end
    return false    
end

local function notify_dizhu_play()
    local dizhu_uid = curr_ddz_instance:get_dizhu_uid()
    local end_time = curr_ddz_instance:get_relative_play_end_time()
    -- if is_player_trustee(dizhu_uid) then
    --     end_time = 0
    -- end
    local msg = {
        next_player_uid = dizhu_uid,
        must_play = 1,
        end_time = end_time,
    }
    notify_others('ddz.NTF_PLAY',nil,msg)
end

local function check_play(curr_time)
    if curr_ddz_instance:is_game_over() then
        return true
    end

    check_trusteed_player(curr_time)

    return false
end

local function pay_lost_coins(uid,coins,player_coins,is_robot)
    if coins > player_coins then
        coins = player_coins
    end
    local ok,succ,ret = R().basesvr({key=uid}):call('.msg_handler', 'pay_lost_coins', uid, coins,
                                                    self_game_type,reason.LOSE_COIN,{is_robot=is_robot},get_extra_bill_info(uid))
    if not ok then
        errlog(uid,'failed to pay_lost_coins',coins)
        return
    end
    if not succ then
        errlog(uid,'failed to pay_lost_coins',coins)
        return
    end

    return {
        curr_coins = ret.curr,
        lost_coins = ret.chged,
    }
end

local function give_won_coins(uid,coins,is_robot)
    local ok,succ,ret = R().basesvr({key=uid}):call('.msg_handler', 'give_won_coins', uid, coins,
                                                    self_game_type,reason.WIN_COIN,{is_robot=is_robot},get_extra_bill_info(uid))
    if not ok then
        errlog(uid,'failed to give_won_coins',coins,succ)
        return
    end
    if not succ then
        errlog(uid,'failed to give_won_coins',coins)
        return
    end

    return {curr_coins = ret.curr,won_coins = ret.chged}
end

-- local function pay_robots_coins(uid,coins)
--     local player_info = assert(player_info_list[uid],'no such player info ' .. tostring(uid))
--     if player_info.coins < coins then
--         coins = player_info.coins
--         player_info.coins = 0
--     else
--         player_info.coins = player_info.coins - coins
--     end

--     return {curr_coins = curr_coins,lost_coins = coins}
-- end

-- local function give_robots_coins(uid,coins)
--     local player_info = assert(player_info_list[uid],'no such player info ' .. uid)
--     player_info.coins = player_info.coins + coins
--     return {curr_coins = player_info.coins,won_coins = coins}
-- end

local function call_take_compensation(uid)
    local ok,given,ret = R().basesvr({key=uid}):call('.msg_handler','take_compensation',uid)
    if not ok then
        errlog(uid,'failed to call_take_compensation')
        return
    end

    if not given then
        return {c = 0}
    end

    return {
        c = ret.compensation_coins,
        times = ret.compensation_times,
        curr_coins = ret.curr_coins
    }
end

local function take_compensation(compensation_list)
    dbglog("==============take_compensation===========")
    if #compensation_list < 1 then
        return
    end
    
    local tasks = {}
    for _,uid in pairs(compensation_list) do
        table_insert(tasks,{ f = call_take_compensation,id = uid,params = {uid} })
    end
    
    local ok,results = cocall(5,tasks)
    if not ok then
        errlog('failed to cocall',tostring_r(results))
        return
    end

    for uid,r in pairs(results) do
        if r then
            local rsp = {
                compensation_times = r.times,
                compensation_coins = r.c,
            }
            player_info_list[uid].coins = r.curr_coins
            if player_info_list[uid].coins == nil then
                dbglog("============== coins==nil ===========")
            end
            send_to_gateway(uid,table_players[uid],'hall.NTF_COMPENSATION',rsp)
        else
            errlog(uid,'failed to check compensation...')
        end
    end
end

local function get_result_info(game_result)
    local result_info = {}
    for _,o in pairs(game_result.winners) do
        local uid = o.uid
        result_info[uid] = o
    end
    for _,o in pairs(game_result.losers) do
        local uid = o.uid
        result_info[uid] = o
    end
    return result_info
end

local function check_chun_tian()
    local chuntian_type,uid = curr_ddz_instance:get_chun_tian_type()
    if chuntian_type > 0 then
        notify_score_and_rate_detail()
    end
    return chuntian_type,uid
end

local function check_task_update(result)
    -- local dizhu_uid = curr_ddz_instance:get_dizhu_uid()
    -- for k,v in pairs(result.winners) do
    --     if not is_robot(v.uid) then
    --         if dizhu_uid ~= v.uid then
    --             add_task_process(v.uid,constant.TASK_FAMER_WIN)
    --         else
    --             add_task_process(v.uid,constant.TASK_DIZHU_WIN)    
    --         end

    --         if table_def.laizi_table_type_map[self_table_type] then
    --             add_task_process(v.uid,constant.TASK_FINISH_LAIZI)
    --         elseif table_def.table_type_map[self_table_type] then
    --             add_task_process(v.uid,constant.TASK_FINISH_STRATEGY)
    --         end
    --     end    
    -- end

    -- for k,v in pairs(result.losers) do
    --     if not is_robot(v.uid) then
    --         if table_def.laizi_table_type_map[self_table_type] then
    --             add_task_process(v.uid,constant.TASK_FINISH_LAIZI)
    --         elseif table_def.table_type_map[self_table_type] then
    --             add_task_process(v.uid,constant.TASK_FINISH_STRATEGY)
    --         end
    --     end
    -- end
end

local function get_player_curr_coins(uid)
    if player_info_list[uid] then
        return player_info_list[uid].coins
    else
        local ok,base_data = R().basesvr({key=uid}):call('.msg_handler','get_base_data',uid)
        if not ok then
            errlog(uid,'get_player_curr_coins-->failed to get base_data')
            return 0
        end

        return base_data.coins
    end

    return 0
end

local function send_real_horse_message(rsp_winner)
    print("=================5555555555555",tostring_r(rsp_winner))
    for k, v in pairs(rsp_winner) do
        local player_info = player_info_list[v.uid]
        if player_info then
            R().mailsvr(1):send('.opration_message_worker','send_real_horse_message',constant.BROADCAST_IDS.BIDS_DDZ,
            {name = player_info.name,coin = v.add_score,vip_level = player_info.vip_level})
        end
    end
end

local function notify_caishenjiadao(uid,coins)
    print("=============notify_caishenjiadao",uid,coins)
    local curr_time = util.get_now_time()
    local countdown_time = global_configs.caishenjiadao.countdown_time or 10
    local end_time = curr_time + countdown_time
    caishen_coins_map[uid] = coins
    send_to_gateway(uid,table_players[uid],'table.NTF_CAISHENJIADAO',
    {end_time = end_time,trigger_coins = coins,game_type = self_game_type})
    billlog({op = "caishenjiadao",type = "trigger",uid = uid,table_type = self_table_type,coins = coins})
end

local function change_robot_store(system_win)
    local roomdata = global_configs.roomdata[self_table_type] or {}
    if not roomdata.robot_type then
        errlog("change_robot_store_conf_error")
        return
    end

    if system_win >= 0 then
        R().exdbsvr(1):send('.system_coins_mgr','add_system_coins',self_game_type,self_table_type,system_win)
    else
        if award_system_flag then --从奖励库存扣钱
            R().exdbsvr(1):send('.system_coins_mgr','reduce_award_coins',self_game_type,self_table_type,math.abs(system_win))
        else --从系统库存扣钱
            R().exdbsvr(1):send('.system_coins_mgr','sub_system_coins',self_game_type,self_table_type,math.abs(system_win))
        end
    end
end

local function add_player_coins_water(winners,losers)
    print("add_player_coins_water============",tostring_r(winners),tostring_r(losers))
    for _,record in pairs(winners) do
        if not is_robot(record.uid) then
            local score = record.add_score + (record.pay_fee or 0)
            R().basesvr{key = record.uid}:send('.msg_handler','add_player_coins_water',record.uid,score)
        end
    end

    for _,record in pairs(losers) do
        if not is_robot(record.uid) then
            R().basesvr{key = record.uid}:send('.msg_handler','add_player_coins_water',record.uid,math.abs(record.add_score))
        end
    end
end

local function check_store_transfer()
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata or not roomdata.open_robot then
        return
    end
 
    local cnf = global_configs.robot_store[self_table_type]
    if not cnf then
         return
    end

    local base_min  = cnf.base_min or 0
    local base_max  = cnf.base_max or 0
    local base_normal = cnf.base_normal or 0
    local data = {base_min = base_min,base_max = base_max,base_normal = base_normal,table_type = self_table_type}
    R().exdbsvr(1):send('.system_coins_mgr','check_store_transfer',self_game_type,data)
 end

local function audit_game_result(game_result)
    --结算
    print_r(game_result)
    local result_info = get_result_info(game_result)
    print_r(result_info)
    local roomdata = global_configs.roomdata
    local limit = roomdata[self_table_type].limit
    local payers = {}
    local winner_fengding_coins = {}
    local winner_coins = 0
    local real_winner_coins = 0
    local total_score = 0
    local is_beyond = false
    local winners = game_result.winners
    local losers = game_result.losers
    for _,o in pairs(winners) do
        local uid = o.uid
        local score = o.add_score
        assert(score >= 0)
        total_score = total_score + score
    end
    assert(total_score > 0,"game_result is error")
    --是否超过限制
    -- if total_score > limit then
    --     is_beyond = true
    -- end

    --玩家最多能赢多少钱
    assert(winners[1],"there is no winner")
    assert(losers[1],"there is no loser")
    local winner = winners[1]
    local winner_uid = winners[1].uid
    local winner_coins = winners[1].add_score
    dbglog("winner[1] coins and win:", player_info_list[winner_uid].coins, winner_coins)
    -- if is_beyond then
    --     winner_coins = math_floor(winners[1].add_score / total_score * limit)
    -- end
    if winner_coins > player_info_list[winner_uid].coins then
        dbglog("add_score > coins:", winner_coins, player_info_list[winner_uid].coins)
        winner_coins = player_info_list[winner_uid].coins
        winner_fengding_coins[winner_uid] = winner_coins
    end
    if winners[2] then
        local uid = winners[2].uid
        local coins2 = winners[2].add_score
        dbglog("winner[2] coins and win:", player_info_list[uid].coins, coins2)
        -- if is_beyond then
        --     coins2 = limit - math_floor(winners[1].add_score / total_score * limit)
        -- end
        if coins2 > player_info_list[uid].coins then
            dbglog("add_score > coins:", coins2, player_info_list[uid].coins)
            coins2 = player_info_list[uid].coins
            winner_fengding_coins[uid] = coins2
        end
        winner_coins = winner_coins + coins2
    end

    -- 赢家只有一个时，赢家赢的金币取偶数，保证每个输家输的金币是整数
    if not winners[2] and winner_coins % 2 ~= 0 then
        winner_coins = winner_coins - 1
        winner_fengding_coins[winner_uid] = winner_coins
    end

    --根据winner_coins去算出输的玩家要扣多少钱
    local loser_uid = losers[1].uid
    local lost_score = -losers[1].add_score
    assert(lost_score >= 0)
    local lost_coins = math_floor(lost_score/total_score * winner_coins)
    dbglog("loser[1] coins and lose:", player_info_list[loser_uid].coins, lost_coins)

    -- 输家只有一个时，输家输的金币取偶数，保证每个赢家赢的金币是整数(整个算法逻辑比较混乱，暂时先这么写)
    if not losers[2] then
        if lost_coins > player_info_list[loser_uid].coins and player_info_list[loser_uid].coins % 2 ~= 0 then
            lost_coins = player_info_list[loser_uid].coins - 1
        end
    end

    local curr_result = {}
    local rsp_winner = {}
    local rsp_loser = {}

    if is_robot(loser_uid) then
        local player_info = player_info_list[loser_uid]
        player_info.coins = player_info.coins - lost_coins
        curr_result[loser_uid] = lost_coins
        real_winner_coins = real_winner_coins + lost_coins
        local pochan = POCHAN_NOT
        if player_info.coins == 0 then
            pochan = POCHAN_YES
        end
        table_insert(rsp_loser,{uid = loser_uid,add_score = -lost_coins,base_score=result_info[loser_uid].base_score,
                                rate = result_info[loser_uid].rate,pochan = pochan})
    else
        table_insert(payers,{ f = pay_lost_coins,id = loser_uid,
                              params = {loser_uid,lost_coins,player_info_list[loser_uid].coins,
                                        is_robot(loser_uid)} })
    end
    if losers[2] then
        local uid = losers[2].uid
        local lost_score = -losers[2].add_score
        local lost_coins = winner_coins - math_floor(-losers[1].add_score/total_score * winner_coins)
        if is_robot(uid) then
            local player_info = player_info_list[uid]
            player_info.coins = player_info.coins - lost_coins
            curr_result[uid] = lost_coins
            real_winner_coins = real_winner_coins + lost_coins
            local pochan = POCHAN_NOT
            if player_info.coins == 0 then
                pochan = POCHAN_YES
            end
            table_insert(rsp_loser,{uid = uid,add_score = -lost_coins,base_score=result_info[uid].base_score,
                                    rate = result_info[uid].rate,pochan = pochan})
        else
            dbglog("loser[2] coins and lose:", player_info_list[uid].coins, lost_coins)
            table_insert(payers,{ f = pay_lost_coins,id = uid,
                                  params = {uid,lost_coins,player_info_list[uid].coins,is_robot(uid)} })
        end
    end

    --扣钱
    local ok,results = cocall(5,payers)
    if not ok then
        errlog('failed to cocall',tostring_r(results))
        return
    end

    dbglog('------------results of losers cocall',tostring_r(results))

    
    --赢家实际上赢多少钱
    for uid,ret in pairs(results) do
        if not ret then
            errlog(uid,'failed to cocall...',tostring_r(results))
            return
        end

        real_winner_coins = real_winner_coins + ret.lost_coins

        local player_info = assert(player_info_list[uid])
        player_info.coins = ret.curr_coins
        curr_result[uid] = -ret.lost_coins
        print("loser lose coins:",uid,ret.lost_coins)
        local pochan = POCHAN_NOT
        if player_info.coins == 0 then
            pochan = POCHAN_YES
        end
        table_insert(rsp_loser,{uid = uid,add_score = -ret.lost_coins,base_score=result_info[uid].base_score,
                                rate = result_info[uid].rate,pochan = pochan})
    end

    local winners = game_result.winners

    local givers = {}

    local win_coins1 = math_floor(real_winner_coins * winners[1].add_score / total_score)
    if win_coins1 > player_info_list[winner_uid].coins then
        win_coins1 = player_info_list[winner_uid].coins
    end
    local results_robot = {}
    if not winners[2] then
        if is_robot(winner_uid) then
            local player_info = player_info_list[winner_uid]
            player_info.coins = player_info.coins + win_coins1 
            results_robot[winner_uid] = {curr_coins = player_info.coins,won_coins = win_coins1}  
        else
            table_insert(givers,{ f = give_won_coins,id = winner_uid,
                                  params = {winner_uid,win_coins1,is_robot(winner_uid)} })
        end
    else
        local uid = winners[2].uid
        local win_coins2 = real_winner_coins - win_coins1
        if win_coins2 > player_info_list[uid].coins then
            win_coins2 = player_info_list[uid].coins
        end
        if win_coins1 + win_coins2 ~= real_winner_coins then
            win_coins1 = real_winner_coins - win_coins2
        end

        if is_robot(winner_uid) then
            local player_info = player_info_list[winner_uid]
            player_info.coins = player_info.coins + win_coins1 
            results_robot[winner_uid] = {curr_coins = player_info.coins,won_coins = win_coins1}  
        else
            table_insert(givers,{ f = give_won_coins,id = winner_uid,
                                  params = {winner_uid,win_coins1,is_robot(winner_uid)} })
        end

        if is_robot(uid) then
            local player_info = player_info_list[uid]
            player_info.coins = player_info.coins + win_coins2 
            results_robot[uid] = {curr_coins = player_info.coins,won_coins = win_coins2}  
        else
            table_insert(givers,{ f = give_won_coins,id = uid,params = {uid,win_coins2,is_robot(uid)} })
        end
    end

    local ok,results = cocall(3,givers)
    if not ok then
        errlog('failed to cocall',tostring_r(results))
    end

    dbglog('------------results of winners cocall',tostring_r(results))

    local roomdata = assert(global_configs.roomdata)
    local cost_rate = assert(roomdata[self_table_type].cost)
    local ticket_list = {}
    local ticket_map  = {}
    local make_rsp_winner = function(uid,ret)
        local player_info = assert(player_info_list[uid])
        local fengding = FENGDING_NOT
        -- if is_beyond then
        --     fengding = FENGDING_CHANGCI
        -- end
        -- if ret.won_coins >= player_info.coins then
        local fengding_coins = winner_fengding_coins[uid]
        dbglog("fengding_coins:", fengding_coins, "won_coins:", ret.won_coins)
        if fengding_coins and ret.won_coins >= fengding_coins then
            fengding = FENGDING_JINBI
        end
        player_info.coins = ret.curr_coins
        curr_result[uid] = ret.won_coins
        dbglog("winner get coins:",uid,ret.won_coins)

        table_insert(rsp_winner,{uid = uid,add_score = ret.won_coins,base_score=result_info[uid].base_score,
                                 rate = result_info[uid].rate,fengding = fengding})
        local cost = math_floor(ret.won_coins * cost_rate / 100)
        dbglog("winner pay cost:", uid, cost)
        table_insert(ticket_list, {uid=uid, cost=cost})
        ticket_map[uid] = cost
    end

    for uid,v in pairs(results_robot) do
        results[uid] = v
    end

    for uid,ret in pairs(results) do
        if ret then
            make_rsp_winner(uid,ret)
            --检查是否触发财神驾到
            --[[local player_info = player_info_list[uid]
            if player_info and not is_robot(uid) then
                local vip_level = player_info.vip_level
                local last_caishen_trigger_time = player_info.last_caishen_trigger_time
                print("===========",uid,ret.won_coins,self_table_type,vip_level or 0)
                if system_store.check_trigger_caishenjiadao(uid,ret.won_coins,self_table_type,vip_level or 0) then
                    
                    notify_caishenjiadao(uid,ret.won_coins)
                end
            end]]
            --添加玩家首冲进度
            print("first charge==================",ret.won_coins,ticket_map[uid])
            -- R().activitysvr(1):send('.activity_mgr','act_add_first_charge_process',uid,ret.won_coins + ticket_map[uid])
        else
            errlog(uid,'failed to cocall...',tostring_r(results))
        end
    end

    ---------------------------结束界面-------------------------------------
    local rsp_left_list = {}
    for uid,o in pairs(player_info_list) do
        local t = {}
        t.uid = uid
        local card_id_list = curr_ddz_instance:get_player_card_ids(uid)
        t.card_suit = card_id_list
        table_insert(rsp_left_list,t)
    end
    local chuntian_type,chuntian_uid = check_chun_tian()
    -- if chuntian_type > 0 then
    --     assert(chuntian_uid)
    --     if not is_robot(chuntian_uid) then
    --         add_task_process(chuntian_uid,constant.TASK_PLAY_CHUNTIAN)
    --     end
    -- end

    --这里要跑一些结算的逻辑
    local score_rate_detail = curr_ddz_instance:get_score_rate_detail()
    local notification = {
        winner_uid_list = rsp_winner,
        loser_uid_list = rsp_loser,
        left_list = rsp_left_list,
        chuntian_type = chuntian_type,
    }
    notify_others('ddz.NTF_GAMEOVER',nil,notification)
    
    send_real_horse_message(rsp_winner)
    ---------任务---------------------
    -- check_task_update(game_result)
    -----------补助---------------------
    -- take_compensation(compensation_list)

    for uid,r in pairs(curr_result) do
        notify_money_changed(uid,{coins = player_info_list[uid].coins})
        notify_others('ddz.NTF_MONEY_CHANGE',nil,{uid = uid,coins = player_info_list[uid].coins})
    end

    pay_all_tickets(ticket_list)

    game_over_time = util.get_now_time()
    local str_date = os.date("%Y%m%d%H%M%S")
    local table_id = self_table_id
    local change_store_coins = 0
    local add_score_map = {}
    local player_win = 0
    local total_num = 0
    local winner_list = utils.shallow_copy(rsp_winner)
    for _,data in pairs(winner_list) do
        data.pay_fee   = ticket_map[data.uid] or 0
        data.is_banker = curr_ddz_instance:is_dizhu(data.uid)
        data.add_score = data.add_score - (ticket_map[data.uid] or 0)
        data.left_score = get_player_curr_coins(data.uid)
        data.is_robot  = false
        if is_robot(data.uid) then
            data.is_robot = true
        else
            player_win = player_win + data.add_score
        end
        add_score_map[data.uid] = data.add_score
        total_num = total_num + 1

        if (control_player_uid or 0) > 0 and data.uid == (control_player_uid or 0) and system_result_type == constant.PERSONAL_STORE_WIN then
            local win_coins = data.add_score + (ticket_map[data.uid] or 0)
            local ok,coins = R().basesvr({key=data.uid}):call('.msg_handler','update_player_control_value',data.uid,win_coins,self_table_type)
            print("update_player_control_value===111",ok,coins)
            gm_store_coins_add = (ok and coins) and coins or 0
        end
    end
    local loser_list  = utils.shallow_copy(rsp_loser)
    for _,data in pairs(loser_list) do
        data.pay_fee  = ticket_map[data.uid] or 0
        data.is_banker = curr_ddz_instance:is_dizhu(data.uid)
        data.left_score = get_player_curr_coins(data.uid)
        data.is_robot = false
        if is_robot(data.uid) then
            data.is_robot = true
        else
            player_win = player_win + data.add_score
        end
        add_score_map[data.uid] = data.add_score
        total_num = total_num + 1
        if (control_player_uid or 0) > 0 and data.uid == (control_player_uid or 0) and system_result_type == constant.PERSONAL_STORE_LOSE then
            local win_coins = data.add_score
            local ok,coins = R().basesvr({key=data.uid}):call('.msg_handler','update_player_control_value',data.uid,win_coins,self_table_type)
            print("update_player_control_value===111",ok,coins)
            gm_store_coins_add = (ok and coins) and -coins or 0
        end
    end

    change_store_coins = player_win - gm_store_coins_add --把在GM库存里面扣的钱减掉
    change_robot_store(-change_store_coins) --增减系统库存

    add_player_coins_water(winner_list,loser_list)
    check_store_transfer() --转移库存
    local side_list = {}
    for position,uid in ipairs(ordered_players) do
        local side_info = {}
        side_info.side = position
        side_info.uid = uid
        side_info.cards = curr_ddz_instance:sort_player_cards(uid)
        side_info.is_banker = curr_ddz_instance:is_dizhu(uid)
        side_info.dizhu_cards = side_info.is_banker and curr_ddz_instance:get_dizhu_card_ids() or {}
        side_info.zhadan_num = curr_ddz_instance:get_player_zhadan_num(uid)
        side_info.add_score = add_score_map[uid] or 0
        side_info.jiaofen = curr_ddz_instance:get_player_robot_dizhu_score(uid)
        side_info.jiabei  = curr_ddz_instance:is_player_jiabei(uid)

        table_insert(side_list,side_info)
    end

    local exinfo = curr_ddz_instance:get_ddz_exinfo()
    exinfo.system_result = system_result_type or 0
    exinfo.control_uid = gm_control_uid or 0

    billlog({op = "card_record",table_gid = str_date .. "_" .. self_table_id,
            table_type = self_table_type,game_type = self_game_type,begin_time = game_start_time,
            end_time = game_over_time,winner_list = winner_list,
            loser_list = loser_list,system_win = -player_win,
            address = skynet.address(skynet.self()),
            side_list = side_list,
            total_num = total_num,
            exinfo = exinfo,
        })

    local system_win_lose = 0
    for _,data in pairs(winner_list) do  
        if data.is_robot == false then
            R().basesvr({key=data.uid}):send('.msg_handler','add_person_water',data.uid,data.add_score + (ticket_map[data.uid] or 0),data.is_robot)   
            system_win_lose = system_win_lose - data.add_score 
            billlog({ op   = "water_history",  
                table_gid  = str_date .. "_" .. self_table_id,
                table_type = self_table_type,
                uid        = data.uid,
                value      = data.add_score + (ticket_map[data.uid] or 0),
                is_robot   = is_robot(data.uid),
                r          = constant.WATER_TYPE.WATER_WIN
            })
            R().basesvr({key=data.uid}):send('.msg_handler','add_today_win_coins',data.uid,data.add_score)
            R().basesvr({key=data.uid}):send('.msg_handler','add_play_count',data.uid,1)
            local player_info = player_info_list[data.uid]
            player_info.all_win_lose = (player_info.all_win_lose or 0) + data.add_score
            R().basesvr({key=data.uid}):send('.msg_handler','add_total_coins',data.uid,data.add_score)
            player_info.all_free = (player_info.all_free or 0) + (ticket_map[data.uid] or 0)
        end
    end

    for _,data in pairs(loser_list) do
        --local curr_coins = get_player_curr_coins(data.uid)
        if data.is_robot == false then
            R().basesvr({key=data.uid}):send('.msg_handler','add_person_water',data.uid,math_abs(data.add_score),data.is_robot)
            system_win_lose = system_win_lose + math_abs(data.add_score)
            billlog({ op   = "water_history",  
                table_gid  = str_date .. "_" .. self_table_id,
                table_type = self_table_type,
                uid        = data.uid,
                value      = math_abs(data.add_score),
                is_robot   = is_robot(uid),
                r          = constant.WATER_TYPE.WATER_LOSE
            })
            R().basesvr({key=data.uid}):send('.msg_handler','add_play_count',data.uid,1)
            local player_info = player_info_list[data.uid]
            player_info.all_win_lose = (player_info.all_win_lose or 0) + data.add_score
            R().basesvr({key=data.uid}):send('.msg_handler','add_total_coins',data.uid,data.add_score)
        end 
        -- if data.is_robot == false and curr_coins <= constant.POCHAN_COINS then --记录破产日志
        --     billlog({op = "bankrupt_register",uid = data.uid,game_type = self_game_type,
        --         table_type = self_table_type,
        --         ret = curr_coins,
        --     })
        -- end
    end
    sendLoseWinToFriendServer(system_win_lose)
end

local function nodizhu_restart()
    for uid,status in pairs(player_status_list) do
        assert(status == PLAYER_STATUS_PLAYING)
        player_status_list[uid] = PLAYER_STATUS_READY
    end
end

local function put_back_robots()
    local robot_ids_list = {}
    for uid, _ in pairs(player_info_list) do
        if is_robot(uid) then
            robot_manager[uid]['on_exit'](robot_manager[uid])
            robot_manager[uid] = nil
            enter_robot_map[uid] = nil 
        end
    end
    -- if #robot_ids_list > 0 then
    --     R().robotmgr(1):send('.msg_handler','put_back_robot',robot_ids_list)
    -- end
end

local function check_stop_table()
    if stopping_server then        
        billlog({op = "stop_table",status = curr_status,password = self_password})
        closing_server = true --直接关闭桌子
        return true
    end

    return false
end

local function check_close_table(curr_time)
    if keep_table then
        return
    end

    local closable = false
    if exit_now then
         closable = true
    end

    if curr_status == TABLE_STATUS_RESTART then
        closable = true
    end
    --如果不满3人，则直接关闭房间
    if #ordered_players == ddz.MAX_PLAYER_NUM and 
        curr_time - start_waiting_for_ready_time < 30 then
            closable = false
    else
        closable = true
    end

    if closing_server then 
        closable = true
    elseif curr_ddz_instance then
        closable = false
    end

    if not closable then
        return
    end

    --检查还有fd的玩家
    for uid,fd in pairs(table_players) do
        if fd > 0 then
            watch_session(fd,uid,false)
        end
    end

    notify_all('ddz.NTF_BACKTO_MATCH',{})
    unlock_all_players()
    put_back_robots()

    local player_ids_list = {}
    for uid, _ in pairs(player_info_list) do
        if not is_robot(uid) then
            table_insert(player_ids_list,uid)
        end
    end

    --这里是可以关闭了
    print('!!!now delete this table!!!!',self_table_type,self_table_id)

    skynet.send('.table_mgr','lua','on_table_delete',self_table_id,player_ids_list)

    skynet.exit()
end

local function leave(uid)
    assert(table_players[uid])

    --假如当前处于待准备中，则直接离开
    if curr_status == TABLE_STATUS_WAITTING_READY then
        realTimeWinLose(uid)
        --清除掉玩家
        table_players[uid] = nil
        local player_info = assert(player_info_list[uid])
        player_info_list[uid] = nil

        local del_index
        for i,_uid in ipairs(ordered_players) do
            if _uid == uid then
                del_index = i
                break
            end
        end
        assert(del_index,'no such index ' .. uid)
        table_remove(ordered_players,del_index)
        unlock_one_player(uid)

        --由check close处去通知其它玩家走人
        return LEAVE_STATUS_REAL
    else
        --假如当前是处于游戏中，则将状态设置为离线状态
        assert(table_players[uid] ~= OFFLINE_CLIENT_FD)
        table_players[uid] = OFFLINE_CLIENT_FD
        return LEAVE_STATUS_OFFLINE
    end
end

local function kick_robot()
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata then
        errlog('no roomdata')
        return
    end

    dbglog("roomdata.min", roomdata.min, "roomdata.max:", roomdata.max)

    for _, uid in ipairs(ordered_players) do
        if is_robot(uid) then
            local is_kick = false
            local player_info = assert(player_info_list[uid])
            dbglog("robot coins:", uid, player_info.coins)
            if player_info.coins < roomdata.min then
                dbglog("kick_robot: coins not enough")
                is_kick = true
            elseif roomdata.max ~= 0 and player_info.coins > roomdata.max then
                dbglog("kick_robot: coins too many")
                is_kick = true
            end
            if is_kick then
                leave(uid)
            end
        end
    end
end

-------------------------------游戏主循环------------------------
function update(curr_time)
--    dbglog('=============curr status',tostring(curr_status))
    if curr_status == TABLE_STATUS_REGISTERED then
        curr_status = TABLE_STATUS_WAITING_ENTER
        waiting_enter_timeout = curr_time + 3
        start_waiting_for_ready_time = curr_time
    elseif curr_status == TABLE_STATUS_WAITING_ENTER then
        if check_all_enter(curr_time) then
            curr_status = TABLE_STATUS_WAITTING_READY
        end
    elseif curr_status == TABLE_STATUS_WAITTING_READY then
        if are_all_players_ready() then
            curr_status = TABLE_STATUS_PAY_TICKET
            --去扣钱
            curr_paid_tickets = nil
            if lock_all_players() then
                --只有上锁成功了才去扣钱
                -- pay_all_tickets()
            else
                errlog('failed to lock_all_players!!!')
            end
        end
    elseif curr_status == TABLE_STATUS_PAY_TICKET then
        if are_all_players_locked() then
            curr_status = TABLE_STATUS_CHECK_START
        end
    elseif curr_status == TABLE_STATUS_CHECK_START then
        if check_start(curr_time) then
            curr_status = TABLE_STATUS_ROB_DIZHU
            --trigger_event('on_start',curr_ddz_instance)
            notify_start(curr_ddz_instance)
            game_start_time = util.get_now_time()
            curr_ddz_instance:setingdizhu_status(ddz.SET_DIZHU_WAY_SCORE,control_win_uid)
            notify_dizhu_status()
            trigger_event('on_start',curr_ddz_instance)
        end
    elseif curr_status == TABLE_STATUS_NODIZHU_RESTART then
        if check_start(curr_time) then
            curr_status = TABLE_STATUS_ROB_DIZHU
            --trigger_event('on_start',curr_ddz_instance)
            notify_start(curr_ddz_instance)
            curr_ddz_instance:setingdizhu_status(ddz.SET_DIZHU_WAY_SCORE,control_win_uid)
            notify_dizhu_status()
            trigger_event('on_start',curr_ddz_instance)
        end
    elseif curr_status == TABLE_STATUS_ROB_DIZHU then
        local over,dizhu_uid = check_rob_dizhu(curr_time)
        if over then
            if dizhu_uid then
                dbglog("==============over TABLE_STATUS_ROB_DIZHU====1=======")
                notify_score_and_rate_detail()
                notify_rob_dizhu_over()
                curr_status = TABLE_STATUS_JIABEI
                trigger_event('on_jiabei')
            else
                dbglog("==============over TABLE_STATUS_ROB_DIZHU====0=======")
                curr_status = TABLE_STATUS_NODIZHU
                nodizhu_times = nodizhu_times + 1
            end
        end
    elseif curr_status == TABLE_STATUS_JIABEI then
        if check_jiabei_over(curr_time) then
            curr_ddz_instance:start_play()
            notify_dizhu_play()
            curr_status = TABLE_STATUS_PLAYING
            trigger_event('on_start_play')
        end    
    elseif curr_status == TABLE_STATUS_PLAYING then
        if check_play(curr_time) then
            curr_status = TABLE_STATUS_GAMEOVER
        end
    elseif curr_status == TABLE_STATUS_GAMEOVER then
        local game_result = finish_game()
        if game_result then
            curr_status = TABLE_STATUS_RESTART
            local ok,ret = xpcall(audit_game_result,debug.traceback,game_result)
            if not ok then
                errlog(ret)
            end
        end
    elseif curr_status == TABLE_STATUS_RESTART then
        curr_ddz_instance = nil
        --正常跑完一局则解散桌子
        check_stop_table()
    elseif curr_status == TABLE_STATUS_NODIZHU then
        curr_ddz_instance = nil
        start_waiting_for_ready_time = curr_time
        nodizhu_restart()
        curr_status = TABLE_STATUS_NODIZHU_RESTART
        trigger_event('on_nodizhu_restart')
        notify_all('ddz.NTF_NODIZHU_RESTART',{})
    else
        errlog('unknown status...',curr_status)
    end

    check_close_table(curr_time)
end

local function game_update()
    curr_status = TABLE_STATUS_REGISTERED
    while true do
        local curr_time = util.get_now_time()
        local ok,ret = xpcall(update,debug.traceback,curr_time)
        if not ok then 
            errlog(ret)
        end
        skynet.sleep(50)
    end
end

----------------------------------------提供服务------------------------------------
local function run_robots()
    for _,AIobj in pairs(robot_manager) do
        skynet.fork(function() AIobj:update() end)
    end
end

local function make_game_robot_config()
    local weigh_value_conf = assert(global_configs.ai_weigh_value) 
    local rob_dizhu_conf = assert(global_configs.rob_dizhu_rate)
    local jia_bei_conf  = assert(global_configs.jia_bei_rate)

    return {
        weigh_value_conf = weigh_value_conf,
        rob_dizhu_conf = rob_dizhu_conf,
        jia_bei_conf = jia_bei_conf,
    }
end

local function load_robot(player_data_list)
    local roomdata = global_configs.roomdata[self_table_type]
    if not roomdata then
        errlog('load_robot fail because roomdata is nil')
        return false
    end

    local robot_num = ddz.MAX_PLAYER_NUM - #player_data_list
    if not roomdata.open_robot or not roomdata.robot_type then
        if robot_num ~= 0 then
            return false
        end
    end

    local robotdata = global_configs.robot[roomdata.robot_type]
    if not robotdata then
        return false
    end

    if robot_num > 0 then
        local robot_type  = roomdata.robot_type
        local conis_limit = {robotdata.min_coin,robotdata.max_coin}
        local ok,succ,ret_map = R().robotmgr(1):call('.msg_handler','get_robots',robot_num,robot_type,conis_limit)
        if not ok then
            errlog("load_robot is fail because ok is nil")
            return false
        end

        if not succ then
            errlog("load_robot is fail because succ is nil")
            return false
        end

        local curr_time = util.get_now_time()
        for _, robot_info in pairs(ret_map) do
            local robot_uid = robot_info.uid
            local player_data = {
                uid = robot_uid,
                match_type = self_table_type,
                begin_time = curr_time,
                name = robot_info.name,
                coins = robot_info.coins,
                sex = robot_info.sex,
                icon = robot_info.icon,
                last_chat_time = 0,
                last_ping_time = curr_time,
                icon_border = robot_info.icon_border,
            }
            table_insert(player_data_list,player_data)
            local conf = make_game_robot_config()
            local robot_obj = game_robot.new(robot_uid,player_data,robot_type,robot_AI,
                                             skynet.self(),ROBOT_CLIENT_FD,conf)
            robot_manager[robot_uid] = robot_obj
        end
    end
    return true
end

local function close_now()
    local curr_time = util.get_now_time()
    check_close_table(curr_time)
end

local function register_all(player_data_list)
    assert(#player_data_list <= ddz.MAX_PLAYER_NUM)

    local result = {failed_list={}, success_list={},
                    table_stats={table_type = self_table_type,
                                 table_gid = this_table_gid,
                                 game_type = self_game_type},
                    no_update = true}

    local human_uids = {}
    for _,player_data in pairs(player_data_list) do
        local uid = player_data.uid
        table_insert(human_uids,uid)
    end

    local roomdata = global_configs.roomdata[self_table_type]
    print("register_all========",roomdata.open_robot,#human_uids)
    if not roomdata.open_robot and #human_uids ~= ddz.MAX_PLAYER_NUM then
        exit_now = true
        for _, player_data in pairs(player_data_list) do
            if utils.is_in_table(human_uids,player_data.uid) then
                table_insert(result.failed_list, player_data)
            end
        end
        skynet.timeout(3,close_now)

        return result
    end

    --加载机器人 只有加载机器人成功了那么才会去锁住玩家进行开始游戏
    if #human_uids ~= ddz.MAX_PLAYER_NUM and not load_robot(player_data_list) then
        exit_now = true
        for _, player_data in pairs(player_data_list) do
            if utils.is_in_table(human_uids,player_data.uid) then
                table_insert(result.failed_list, player_data)
            end
        end
        skynet.timeout(3,close_now)

        return result
    end

    if not lock_all_players_on_register(human_uids) then
        errlog('failed to lock_all_players')
        for _, player_data in pairs(player_data_list) do
            if utils.is_in_table(human_uids,player_data.uid) then
                table_insert(result.failed_list, player_data)
            end
        end
        --失败了要归还机器人
        put_back_robots()
        return result
    end

    -- local names_library = global_configs.names_library
    -- --检查并增加陪玩机器人
    -- local robots_ids = {1999999991,1999999992}
    -- for i=#player_data_list + 1,ddz.MAX_PLAYER_NUM do
    --     local uid = assert(table_remove(robots_ids))
    --     local player_data = {
    --         uid = uid,
    --         name = names_library[math_random(1,#names_library)],
    --         coins = math_random(9999,1999999),
    --         win_times = math_random(10,1000),
    --         failure_times = math_random(10,1000),
    --         has_card_note = false,
    --         sex = math_random(1,2),
    --     }
    --     player_data.icon = tostring(player_data.sex)

    --     table_insert(player_data_list,player_data)
    --     local conf = make_game_robot_config()
    --     local robot_obj = game_robot.new(uid,robot_AI,skynet.self(),ROBOT_CLIENT_FD,conf)
    --     robot_manager[uid] = robot_obj
    -- end

    assert(not next(player_info_list))
    local curr_time = util.get_now_time()
    for _,player_data in pairs(player_data_list) do
        local uid = player_data.uid
        assert(not table_players[uid])
        table_players[uid] = REGISTER_CLIENT_FD
        --模式为4的时候表示防刷模式
        if self_table_pattern == 4 and not robot_manager[uid] then
            limit_uid = uid
        end
        table_insert(ordered_players,uid)
        
        player_data.position = #ordered_players
        player_data.last_chat_time = 0
        player_data.last_ping_time = curr_time
        player_data.init_coins = player_data.coins
        player_data.all_win_lose = 0
        player_data.all_free = 0
        player_info_list[uid] = player_data
        player_status_list[uid] = PLAYER_STATUS_READY   --默认进来就是准备了

        player_data.mingpai_status = MINGPAI_STATUS_NO
    end
    assert(#ordered_players == ddz.MAX_PLAYER_NUM)


    trigger_event('on_register')

    skynet.fork(game_update)

    run_robots()

    for _, player_data in pairs(player_data_list) do
        table_insert(result.success_list, player_data.uid)
    end
    return result
end

local function robot_play_cards(uid,card_suit,card_suit_type)
    if not curr_ddz_instance or not card_suit or #card_suit < 1 then
        return
    end
    --一次性打出6张牌以上
    if #card_suit > 6 then
        trigger_event('on_use_picture',uid,global_configs.robot_picture.ddz.more_cards)
    end
    if card_suit_type ~= ddz.CARD_SUIT_TYPE_WANGZHA and card_suit_type ~= ddz.CARD_SUIT_TYPE_ZHADAN 
        and card_suit_type ~= ddz.CARD_SUIT_TYPE_RUANZHA then
         return
    end
    for _uid,info in pairs(player_info_list) do
        if is_robot(_uid) and _uid ~= uid then
            if not curr_ddz_instance:is_dizhu(uid) and not curr_ddz_instance:is_dizhu(_uid) then
                --友方
                trigger_event('on_use_picture',_uid,global_configs.robot_picture.ddz.friend_boom)
            else
                --敌方
                trigger_event('on_use_picture',_uid,global_configs.robot_picture.ddz.enemy_boom)
            end
        end
    end
end

function internal.register_all(player_data_list)
    return register_all(player_data_list)
end

function internal.get_player_num(...)
    return get_player_num()
end

internal.newmatch_register_all = register_all

function internal.sync_table_stats()
    return nil
end

---------------------------客户端请求类处理----------------------------------------
function handler.REQ_ROBDIZHU(uid,req_msg)
    if curr_status ~= TABLE_STATUS_ROB_DIZHU then
        errlog(uid,'can not rob dizhu')
        return
    end
    
    local score = req_msg.score
    if score <0 or score >3 then
        errlog('invalid score',score)
        return
    end

    if req_msg.is_rob ==1 and score <= rob_dizhu_score then
        errlog('invalid score',score)
        return
    end

    local msg = { result = 0 }
    local fd = table_players[uid]
    local ret,error_code = rob_dizhu(uid,score,req_msg.is_rob == 1)
    if not ret  then
        msg.result = error_code
        send_to_gateway(uid,fd,'ddz.RSP_ROBDIZHU',msg)
        return
    end

    if req_msg.is_rob == 1 or req_msg.score > 0 then
        notify_score_and_rate_detail()
    end
    return true
end

function handler.REQ_PLAY(uid,msg)
--    print("===============1111handler.REQ_PLAY====",tostring_r(msg))
     if curr_status ~= TABLE_STATUS_PLAYING then
        errlog("curr_status is not playing !! ",curr_status)
        return
    end

    if not curr_ddz_instance then
        return
    end

    local fd = table_players[uid]
    local ok,rsp = play(uid,msg.card_suit or {},msg.card_suit_type,msg.card_suit_key)
    if not ok then
        errlog("REQ_PLAY ERROR ",msg,rsp)
        send_to_gateway(uid,fd,'ddz.RSP_PLAY',rsp)
        return true
    end

    set_trustee(uid)
    
    --出牌成功响应
    send_to_gateway(uid,fd,'ddz.RSP_PLAY',rsp)

    local msg = {
        player_uid = uid,
        card_suit_type = rsp.card_suit_type,
        card_suit = rsp.card_suit,
        next_player_uid = rsp.next_player_uid,
        must_play = rsp.must_play,
        end_time = rsp.end_time,
        card_suit_key = rsp.card_suit_key,
        original_card_suit = rsp.original_card_suit,
        over = rsp.over,
    }
 --   print("===============2222222222handler.REQ_PLAY====",tostring_r(msg))
    notify_others('ddz.NTF_PLAY',uid,msg)
    robot_play_cards(uid,rsp.card_suit,rsp.card_suit_type)

    return true
end

function handler.REQ_READY(uid,msg,game_session)
    --准备的时候也需要检查一下金币是否足够
    if not can_ready(uid) then
        send_to_gateway(uid,game_session,'ddz.RSP_READY',
            {result = error_code.GOLD_IS_NOT_ENOUGH})
        return
    end

    set_ready(uid)

    send_to_gateway(uid,game_session,'ddz.RSP_READY',{})
    
    return true
end

function handler.REQ_TRUSTEE(uid,msg)
    local setting = msg.trust > 0
    
    set_trustee(uid,setting)

    local state = 0
    if trusteed_players[uid] then
        state = 1
    end

    send_to_gateway(uid,table_players[uid],'ddz.RSP_TRUSTEE',{state = state})

    notify_event_status(uid)

    return true
end

function handler.REQ_CHAT(uid,msg)
    local player_info = assert(player_info_list[uid])
    if not player_info then
        errlog(uid,"REQ_CHAT failed you are not a player_info")
        return false
    end

    if not player_info.position then
        errlog(uid,"REQ_CHAT not have position")
        return false
    end

    local curr_time = util.get_now_time()

    if player_info.last_chat_time and curr_time - player_info.last_chat_time < 1 then
        errlog('chatting too fast ...',uid)
        send_to_gateway(uid,table_players[uid],'table.RSP_CHAT',{result = error_code.REQ_CHAT_TOO_FAST})
        return
    end

    player_info.last_chat_time = curr_time

    send_to_gateway(uid,table_players[uid],'table.RSP_CHAT',{result = error_code.RSP_SUCESS})

    local ntf = {
        uid = uid,
        type = msg.type,
        picture_id = msg.picture_id,
        content = msg.content,
        voice = msg.voice,
        content_id = msg.content_id
    }

    notify_all('table.NTF_CHAT',ntf)

    return true
end

function handler.REQ_LEAVE(uid,msg,game_session)
    if table_players[uid] ~= game_session then
        send_to_gateway(uid,game_session,'ddz.RSP_LEAVE',{result = -11})
        errlog(uid,'invalid status',table_players[uid],game_session)
        return
    end

    if not player_info_list[uid] then
        send_to_gateway(uid,game_session,'ddz.RSP_LEAVE',{result = -12})
        return
    end
    local init_coins = player_info_list[uid].init_coins or 0

    local ret = leave(uid)

    send_to_gateway(uid,game_session,'ddz.RSP_LEAVE',{status = ret})
    watch_session(game_session,uid,false)
    skynet.send('.table_mgr','lua','leave',uid)

    if ret == LEAVE_STATUS_REAL and not is_robot(uid) then
        R().hallsvr({key=uid}):send('.msg_handler', 'check_lucky_tigger',uid,self_game_type,init_coins)
    end

    return true
end

local function update_player_coins(uid, coins)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.coins = coins
    else
        errlog(string_format("uid(%d) coins(%d) player info not exist", uid, coins))
    end
end

local function reduce_coins(uid, money, reason, ...)
    local robot = is_robot(uid)
    local ok, succ, ret = R().basesvr({ key = uid }):call(".msg_handler", "reduce_coins", uid, money, reason,  {is_robot=robot},get_extra_bill_info(uid) )
    if not ok then
        errlog(string_format("uid(%d) call basesvr reduce coins error", uid))
        return false
    end
    if not succ then
        errlog(string_format("uid(%d) basesvr reduce coins succ == false error", uid))
        return false
    end

    --update_player_coins(uid, ret.curr)
    return ret
end

local function add_coins(uid, money, reason, ...)
    local robot = is_robot(uid)
    local ok, succ, ret = R().basesvr({ key = uid }):call(".msg_handler", "add_coins", uid, money, reason, {is_robot=robot},get_extra_bill_info(uid))
    if not ok then
        errlog(string_format("uid(%d) call basesvr reduce coins error", uid))
        return false
    end
    if not succ then
        errlog(string_format("uid(%d) basesvr reduce coins succ == false error", uid))
        return false
    end

    --update_player_coins(uid, ret.curr)
    return ret
end

--请求使用道具
function handler.REQ_USE_MAGIC_PICTURE(uid,msg,game_session)
    local src_player_info = player_info_list[uid]
    if not src_player_info then
        errlog(uid,"REQ_USE_MAGIC_PICTURE failed you are not a src_player_info")
        return false
    end

    if not src_player_info.position then
        send_to_gateway(uid,table_players[uid],'table.RSP_USE_MAGIC_PICTURE',{result = error_code.USE_ITEM_NEED_POSTION})
        return false
    end

    local dst_player_info = player_info_list[msg.dst_id]
    if not dst_player_info then
        errlog(msg.dst_id,"REQ_USE_MAGIC_PICTURE failed you are not a dst_player_info")
        return false
    end

    if not dst_player_info.position then
        errlog(uid,"REQ_USE_MAGIC_PICTURE not have position")
        return false
    end

    -- local ret = reduce_coins(uid,constant.USE_MAGIC_PICTURE_COIN,reason.USE_MAGIC_PICTRUE)
    -- if not ret then
    --     send_to_gateway(uid,table_players[uid],'table.RSP_USE_MAGIC_PICTURE',{result = error_code.GOLD_IS_NOT_ENOUGH})
    --     return false
    -- end

    -- src_player_info.coins = ret.curr

    send_to_gateway(uid,table_players[uid],'table.RSP_USE_MAGIC_PICTURE',{result = error_code.RSP_SUCESS})

    notify_all('table.NTF_USE_MAGIC_PICTURE',{src_id = uid, dst_id = msg.dst_id,
                                              picture_id = msg.picture_id, coins = src_player_info.coins})

    -- utils.notify_money_changed(uid, { coins = ret.curr }, game_session)

    return true
end


local function get_card_note_info(uid,ddz_instance)
    local other_player_cards = ddz_instance:get_other_player_cards(uid)
    local other_player_record = {}

    local records = ddz_instance:get_player_record()
    for _uid,player_record in pairs(records) do
        if uid ~= _uid then
            local record = { uid = _uid,player_records = {} }
            for _,card_record in pairs(player_record) do
                table_insert(record.player_records,{card_list = card_record})
            end

            table_insert(other_player_record,record)
        end
    end

    return {other_player_cards = other_player_cards,other_player_record = other_player_record}
end

function handler.REQ_CARD_NOTE(uid,msg)
    if not player_info_list[uid].has_card_note then
        local rsp = { result = error_code.HAS_NOT_CARD_NOTE }
        send_to_gateway(uid,table_players[uid],'ddz.RSP_CARD_NOTE',rsp)
        return
    end

    local card_note = {}
    if curr_ddz_instance then
        card_note = get_card_note_info(uid,curr_ddz_instance)
    end
    local rsp = {result = 0,card_note = card_note}
    send_to_gateway(uid,table_players[uid],'ddz.RSP_CARD_NOTE',rsp)

    return true
end

function internal.disconnect(uid,game_session)
    dbglog(uid,'disconnect...',table_players[uid],game_session)
    if table_players[uid] ~= game_session then
        errlog(uid,'invalid status',table_players[uid],game_session)
        return
    end

    local ret = leave(uid)
    dbglog(uid,'disconnect...',ret)
    return true
end

function internal.close_server()
    closing_server = true
end

function internal.stop_server()
    stopping_server = true
end

function internal.enter(uid,game_session,msg)
    local ret,r = enter(uid,game_session,msg)
    if not ret then
        send_to_gateway(uid,game_session,'table.RSP_ENTER',{result = -100})
        return false
    end

    r.result = 0
    send_to_gateway(uid,game_session,'table.RSP_ENTER',r)

    local update_trustee = is_trusteed(uid)
    set_trustee(uid,false)

    if update_trustee then
        notify_event_status(uid)
    end

    notify_player_enter(uid)
    trigger_event('on_use_picture',uid,global_configs.robot_picture.robot_enter,true)
    watch_session(game_session,uid,true)

    return true
end

function internal.start(conf)
    self_table_id = conf.table_id
    self_table_type = conf.table_type
    this_table_gid = conf.table_gid
    self_game_type = conf.game_type
    self_table_pattern = conf.table_conf.table_pattern
    print("self_table_pattern",self_table_pattern,tostring_r(conf))
    dbglog(string.format('table<%d>,type(%d),gid(%d),game_type(%d) got start',
                         self_table_id,self_table_type,this_table_gid,self_game_type))
    return true
end

function internal.exit()
    skynet.exit()
end

function internal.update_coins_on_table(uid,amount,safe_box)
    local player_info = assert(player_info_list[uid])
    player_info.coins = player_info.coins + amount
    notify_others('ddz.NTF_MONEY_CHANGE',uid,{uid = uid,coins = player_info.coins})
    if safe_box then
        player_info.safe_box = safe_box
    end
    return true
end

function internal.get_game_type()
    return self_game_type,self_table_type
end

local function get_non_robot_players_num()
    local player_num = 0

    for _,uid in pairs(ordered_players) do
        if not is_robot(uid) then
            player_num = player_num + 1
        end        
    end

    return player_num    
end

function internal.get_user_playing_info()
    local playing_info = {}
    playing_info.game_type = self_game_type
    playing_info.table_type = self_table_type
    playing_info.total = get_non_robot_players_num()

    return playing_info
end

function handler.REQ_PLAYER_INFO(uid,msg)
    local player_uid = msg.uid
    local player_info = assert(player_info_list[player_uid])
    
    local rsp
    local win_percent = 0
    if is_robot(player_uid) then
        if player_info.win_times + player_info.failure_times > 0 then
            win_percent = math_floor(player_info.win_times / (player_info.win_times + player_info.failure_times)*100)
        end
        rsp = {
            name = player_info.name,
            coins = player_info.coins,
            total_count = player_info.win_times + player_info.failure_times,
            win_percent = win_percent,
            sex = player_info.sex,
            icon = player_info.icon
        }
    else
        local ok,base_data = R().basesvr({key=player_uid}):call('.msg_handler','get_base_data',player_uid)
        if not ok then
            errlog(uid,'failed to get_base_data info',player_uid)
        end
        if base_data.win_times + base_data.losing_times > 0 then
            win_percent = math_floor(base_data.win_times/(base_data.win_times + base_data.losing_times)*100)
        end
        rsp = {
            name = player_info.name,
            coins = base_data.coins,
            total_count = base_data.win_times + base_data.losing_times,
            win_percent = win_percent,
            sex = player_info.sex,
            icon = player_info.icon
        }
    end
    send_to_gateway(uid,table_players[uid],'ddz.RSP_PLAYER_INFO',rsp)
    return true    
end

function handler.REQ_JIABEI(uid,msg)
    if msg.type ~= JIABEI_TYPE_GIVEUP and msg.type ~= JIABEI_TYPE_PUTONG
       and msg.type ~= JIABEI_TYPE_CHAOJI then
       errlog("jiabei input err",msg.type)
       return
    end

    if curr_status ~= TABLE_STATUS_JIABEI then
        errlog("curr_status is not TABLE_STATUS_JIABEI!!")
        return
    end

    jiabei(uid,msg.type)
    send_to_gateway(uid,table_players[uid],'ddz.RSP_JIABEI',{result = 0,type = msg.type})
    
    return true    
end

local function check_have_enough_coins(uid,coins)
    local player_info = player_info_list[uid]
    if not player_info or player_info.coins < coins then
        return false
    end
    return true
end

local function caishen_coins(uid, coins, award_coins, ...)
    local robot = is_robot(uid)
    local ok, succ, ret = R().basesvr({ key = uid }):call(".msg_handler", "caishen_coins", uid, coins, award_coins, {is_robot=robot},get_extra_bill_info(uid))
    if not ok then
        errlog(string_format("uid(%d) call basesvr caishen_coins coins error", uid))
        return false
    end
    if not succ then
        errlog(string_format("uid(%d) basesvr caishen_coins coins succ == false error", uid))
        return false
    end

    --update_player_coins(uid, ret.curr)
    return ret
end

local function pay_caishen_coins(uid,coins)
    local caishenjiadao_store = global_configs.caishenjiadao_store
    local fee_rate = caishenjiadao_store.fee_rate
    local sysback_coins = math_floor((coins *fee_rate) / 10000)
    local base_coins = coins - sysback_coins
    local curr_coins
    local ret_coins = system_store.check_caishen_store_result(self_table_type,uid,coins)
    if ret_coins then
        print("add coins",ret_coins)
        curr_coins = caishen_coins(uid,coins,ret_coins)
        if not curr_coins then
            errlog("reduce coins failed",ret )
            return error_code.NOT_ENOUGH_COINS
        end
        sendLoseWinToFriendServer(coins - ret_coins)
        --改变财神库存
        R().exdbsvr(1):send('.caishen_mgr','push_data_pool',self_table_type,uid,base_coins,sysback_coins)
        R().exdbsvr(1):send('.caishen_mgr','sub_caishen_base',self_table_type,uid,ret_coins)
    else
        local ret = reduce_coins(uid, coins, reason.OPEN_CAISHEN)
        if not ret then
            errlog("reduce coins failed",ret )
            return error_code.NOT_ENOUGH_COINS
        end
        sendLoseWinToFriendServer(coins)
        --改变财神库存
        R().exdbsvr(1):send('.caishen_mgr','push_data_pool',self_table_type,uid,base_coins,sysback_coins)
        curr_coins = ret.curr
    end

    utils.notify_money_changed(uid,{coins = curr_coins})
    notify_all("table.NTF_MONEY_CHANGED", { chged_list = { { uid = uid, coins = curr_coins } } })
    
    update_player_coins(uid, curr_coins)
    caishen_coins_map[uid] = nil
    return error_code.RSP_SUCESS,ret_coins    
end

function handler.REQ_OPEN_CAISHEN(uid,msg)
    local coins = msg.coins
    --[[if not caishen_coins_map[uid] or caishen_coins_map[uid] ~= coins then
        errlog("put coins error",uid,caishen_coins_map[uid])
        return
    end]]
    if not caishen_coins_map[uid] or coins ~= caishen_coins_map[uid] then
        errlog("your param is error",uid,coins)
        send_to_gateway(uid,table_players[uid],'table.RSP_OPEN_CAISHEN',{result = error_code.INPUT_ERROR})
        return false
    end
    
    if not check_have_enough_coins(uid,coins) then
        errlog("you have not enough coins",uid)
        send_to_gateway(uid,table_players[uid],'table.RSP_OPEN_CAISHEN',{result = error_code.NOT_ENOUGH_COINS})
        return false
    end

    local res,ret_coins = pay_caishen_coins(uid,coins)
    if res == error_code.RSP_SUCESS then
        send_to_gateway(uid,table_players[uid],'table.RSP_OPEN_CAISHEN',{result = res,award_coins = ret_coins})
    else
        send_to_gateway(uid,table_players[uid],'table.RSP_OPEN_CAISHEN',{result = res})
    end
end

function handler.REQ_PERSONAL_INFO(uid,msg,game_session)
    local player_info = player_info_list[msg.uid]
    local ret = {}
    if player_info then
        local personal_info = {
            uid = player_info.uid,
            name = player_info.name,
            sex = player_info.sex,
            icon = player_info.icon,
            coins = player_info.coins,
            is_self = false,
            vip_level = player_info.vip_level or 0,
            icon_border = player_info.icon_border,
        }
        ret.personal_info = personal_info
        ret.is_set_down = player_info.position and true or false
        send_to_gateway(uid,game_session,'table.RSP_PERSONAL_INFO',ret)
    else
        send_to_gateway(uid,game_session,'table.RSP_PERSONAL_INFO',{result = error_code.NO_SUCH_PLAYER})
    end
    return true
end

--=================================table protocols=================
local function update_player_heartbeat(uid)
    local player_info = player_info_list[uid]
    if player_info then
        player_info.last_ping_time = util.get_now_time()
    end
end

local function get_msg_module_name(msgid)
    local m = msgdef.id_to_name[msgid]
    if not m then return end
    return m[1],m[2] --[1]->module,[2]->name
end

local function dispatch_client_message(game_session,uid,msg,size)
    local _,msgid,pbmsg,pbsize = proxypack.unpack_client_message(msg,size)
    local module,name = get_msg_module_name(msgid)

    if not module or not name then
        print('invalid msgid',msgid,module,name)
        return
    end

    local pbname = module .. '.' .. name
    local req_msg = pb.decode(pbname,pbmsg,pbsize)

  --  dbglog(string.format('[%s]>>>>>player[%s] got a request[%s] content(%s)',
    --   skynet.address(skynet.self()),tostring(uid),pbname,tostring_r(req_msg)))

    local f = handler[name]
    if not f then
        errlog('unknown action',pbname)
        return
    end

    local ret = f(uid,req_msg,game_session)
    if not ret then
        errlog(string.format('failed to handle requrest(%s.%s)',module,name))
    end

    update_player_heartbeat(uid)
end

function dispatch(_,_,game_session,uid,data)
    local msg,size = proxypack.pack_raw(data)
    local ok,errmsg = xpcall(dispatch_client_message,debug.traceback,game_session,uid,msg,size)
    skynet.trash(msg,size)  --这里需要保证内存被释放
    if not ok then 
        errlog(errmsg)
    end
end

function handle_lua_req(session,source,action,...)
    dbglog('internal request',action,...)
    local f = internal[action]
    if f then
        skynet.retpack(f(...))
    else
        handler[action](...)
    end
end

function start()
    skynet.register_protocol {
        name = "client",
        id = skynet.PTYPE_CLIENT,
        unpack = skynet.unpack,
        dispatch = function(...) return dispatch(...) end
    }

    skynet.start(function()
        skynet.dispatch("lua",function(session,source,action,...)
            handle_lua_req(session,source,action,...)
        end)

        load_config()
    end)
end

return _ENV
